﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Windows.Documents;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Data;
using SHomeWorkshop.LunarMind.Converts;
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;
using SHomeWorkshop.LunarMind.Adorners;
using SHomeWorkshop.LunarMind.Commands;
using System.Windows.Shapes;
using LunarSF.SHomeWorkshop.LunarMind;
using SHomeWorkshop.LunarConcept.Controls;
using SHomeWorkshop.LunarConcept;
using System.Collections;

namespace SHomeWorkshop.LunarMind
{
    public class Title : AdornerDecorator, INotifyPropertyChanged
    {
        private static ContextMenu contextMenu;

        public static void ShowContextMenu()
        {
            if (Globals.MainWindow.MainSelectedTitle != null)
                Globals.MainWindow.MainSelectedTitle.ContextMenu.IsOpen = true;
        }

        static Title()
        {
            contextMenu = new ContextMenu();

            MenuItem miSwitchDirection = new MenuItem() { Header = "左右切换(_D)", InputGestureText = "Ctrl+R", };
            miSwitchDirection.Click += miSwitchDirection_Click;
            contextMenu.Items.Add(miSwitchDirection);

            contextMenu.Items.Add(new Separator());

            MenuItem miEditTitle = new MenuItem() { Header = "编辑文本(_E)", InputGestureText = "Enter", };
            miEditTitle.Click += miEditTitle_Click;
            contextMenu.Items.Add(miEditTitle);

            MenuItem miEditTitleComment = new MenuItem() { Header = "编辑标题备注(_C)", InputGestureText = "Ctrl+Enter", };
            miEditTitleComment.Click += miEditTitleComment_Click;
            contextMenu.Items.Add(miEditTitleComment);

            MenuItem miEditTitleBackgroundQuestionsText = new MenuItem() { Header = "编辑后台试题文本(_Q)", InputGestureText = "Ctrl+Shift+Enter", };
            miEditTitleBackgroundQuestionsText.Click += miEditTitleBackgroundQuestionsText_Click;
            contextMenu.Items.Add(miEditTitleBackgroundQuestionsText);

            MenuItem miPresentateBackgroundQuestions = new MenuItem() { Header = "查看标题中嵌入的试题(_Q)", InputGestureText = "F7", };
            miPresentateBackgroundQuestions.Click += miPresentateBackgroundQuestions_Click;
            contextMenu.Items.Add(miPresentateBackgroundQuestions);

            contextMenu.Items.Add(new Separator());

            MenuItem miInsertSameLevelTitle = new MenuItem() { Header = "插入同级标题(_S)", InputGestureText = "Shift+Enter", };
            miInsertSameLevelTitle.Click += miInsertSameLevelTitle_Click;
            contextMenu.Items.Add(miInsertSameLevelTitle);

            MenuItem miInsertLowerLevelTitle = new MenuItem() { Header = "插入下级标题(_L)", InputGestureText = "Tab|Insert", };
            miInsertLowerLevelTitle.Click += miInsertLowerLevelTitle_Click;
            contextMenu.Items.Add(miInsertLowerLevelTitle);

            MenuItem miAddSummaryComment = new MenuItem() { Header = "给标题添加概要(_S)", InputGestureText = "Ctrl+D|Ctrl+M", };
            miAddSummaryComment.Click += miAddSummaryComment_Click;
            contextMenu.Items.Add(miAddSummaryComment);

            contextMenu.Items.Add(new Separator());

            MenuItem miSwitchCollapseOrExpand = new MenuItem() { Header = "折叠｜展开 下级标题(_C)", InputGestureText = "Alt+C", };
            miSwitchCollapseOrExpand.Click += miSwitchCollapseOrExpand_Click;
            contextMenu.Items.Add(miSwitchCollapseOrExpand);

            MenuItem miSwitchOutBorderVisible = new MenuItem() { Header = "显示｜隐藏 外边框(_O)", InputGestureText = "Ctrl+Shift+B" };
            miSwitchOutBorderVisible.Click += miSwitchOutBorderVisible_Click;
            contextMenu.Items.Add(miSwitchOutBorderVisible);

            contextMenu.Items.Add(new Separator());

            MenuItem miTextDock = new MenuItem() { Header = "文本停靠位置(_D)", };

            MenuItem miTextDockLeft = new MenuItem() { Header = "在图标区域左侧(_L)", InputGestureText = "Shift + ←", };
            miTextDockLeft.Click += miTextDockLeft_Click;

            MenuItem miTextDockRight = new MenuItem() { Header = "在图标区域右侧(_R)", InputGestureText = "Shift + →", };
            miTextDockRight.Click += miTextDockRight_Click;

            MenuItem miTextDockTop = new MenuItem() { Header = "在图标区域顶侧(_T)", InputGestureText = "Shift + ↑", };
            miTextDockTop.Click += miTextDockTop_Click;

            MenuItem miTextDockBottom = new MenuItem() { Header = "在图标区域底侧(_B)", InputGestureText = "Shift + ↓", };
            miTextDockBottom.Click += miTextDockBottom_Click;

            miTextDock.Items.Add(miTextDockLeft);
            miTextDock.Items.Add(miTextDockRight);
            miTextDock.Items.Add(miTextDockTop);
            miTextDock.Items.Add(miTextDockBottom);
            contextMenu.Items.Add(miTextDock);

            contextMenu.Items.Add(new Separator());

            MenuItem miInsertTitleImage = new MenuItem() { Header = "在标题中插入图像(_I)", InputGestureText = "Ctrl+I", };
            miInsertTitleImage.Click += miInsertTitleImage_Click;
            contextMenu.Items.Add(miInsertTitleImage);

            MenuItem miPresentateImagesInTitle = new MenuItem() { Header = "查看标题中的图像(_P)", InputGestureText = "F6", };
            miPresentateImagesInTitle.Click += miPresentateImagesInTitle_Click;
            contextMenu.Items.Add(miPresentateImagesInTitle);

            MenuItem miPasteIconXamlText = new MenuItem() { Header = "粘贴 Lunar Concept 页(_X)", };
            miPasteIconXamlText.Click += miPasteIconXamlText_Click;

            MenuItem miClearXamlText = new MenuItem() { Header = "清除 Lunar Concept 页(_C)", };
            miClearXamlText.Click += MiClearXamlText_Click;

            MenuItem miCopyXamlText = new MenuItem() { Header = "复制 Lunar Concept 页(_O)", };
            miCopyXamlText.Click += MiCopyXamlText_Click;

            //MenuItem miIconXamlTextHelp = new MenuItem() { Header = "关于Xaml对象的说明(_H)", };
            //miIconXamlTextHelp.Click += miIconXamlTextHelp_Click;

            contextMenu.Items.Add(new Separator());

            contextMenu.Items.Add(miPasteIconXamlText);
            contextMenu.Items.Add(miClearXamlText);
            contextMenu.Items.Add(miCopyXamlText);
            //contextMenu.Items.Add(miIconXamlTextHelp);
        }

        private static void MiCopyXamlText_Click(object sender, RoutedEventArgs e)
        {
            var mainSelTitle = Globals.MainWindow.MainSelectedTitle;
            if (mainSelTitle == null) return;

            var node = mainSelTitle.xmlData.SelectSingleNode("IconXamlText");
            if (node == null) return;

            Clipboard.SetData(DataFormats.UnicodeText, "<LunarMind.Xaml>" + node.InnerXml + "</LunarMind.Xaml>");
        }

        private static void MiClearXamlText_Click(object sender, RoutedEventArgs e)
        {
            Commands.SetTitleIconXamlTextCommand.ClearXamlTextOfTitle();
        }

        private

        static void miPasteIconXamlText_Click(object sender, RoutedEventArgs e)
        {
            Commands.SetTitleIconXamlTextCommand.PasteXamlTextToTitle();
        }

        //static void miIconXamlTextHelp_Click(object sender, RoutedEventArgs e)
        //{
        //    MessageBox.Show("　　如果用户懂WPF编程，完全可以自己编写Xaml来实现丰富的功能。配合《Syncfusion Metro Studio 2》使用是极方便的。",
        //        Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
        //}

        static void miTextDockBottom_Click(object sender, RoutedEventArgs e)
        {
            Commands.SetTextDockBottomCommand.SetTextDockBottom();
        }

        static void miTextDockTop_Click(object sender, RoutedEventArgs e)
        {
            Commands.SetTextDockTopCommand.SetTextDockTop();
        }

        static void miTextDockRight_Click(object sender, RoutedEventArgs e)
        {
            Commands.SetTextDockRightCommand.SetTextDockRight();
        }

        static void miTextDockLeft_Click(object sender, RoutedEventArgs e)
        {
            Commands.SetTextDockLeftCommand.SetTextDockLeft();
        }

        static void miSwitchDirection_Click(object sender, RoutedEventArgs e)
        {
            Commands.SwitchDirectionCommand.SwitchTitleDirection();
        }

        static void miAddSummaryComment_Click(object sender, RoutedEventArgs e)
        {
            Commands.InsertSummaryCommentCommand.AddSummaryComment();
        }

        static void miSwitchOutBorderVisible_Click(object sender, RoutedEventArgs e)
        {
            Commands.SwitchTitleOutBorderCommand.Execute();
        }

        static void miSwitchCollapseOrExpand_Click(object sender, RoutedEventArgs e)
        {
            Commands.SwitchTitleCollapsedCommand.SwitchTitleCollapsed();
        }

        static void miPresentateBackgroundQuestions_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.MainSelectedTitle != null)
            {
                Globals.MainWindow.MainSelectedTitle.PresentateQuestions();
            }
        }

        static void miPresentateImagesInTitle_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.MainSelectedTitle != null)
            {
                Globals.MainWindow.MainSelectedTitle.PresentateImages();
            }
        }

        static void miInsertTitleImage_Click(object sender, RoutedEventArgs e)
        {
            Commands.InsertTitleImageCommand.InsertTitleImage();
        }

        static void miInsertLowerLevelTitle_Click(object sender, RoutedEventArgs e)
        {
            Commands.InsertLowerLevelTitleCommand.InsertLowerLevelTitle();
        }

        static void miEditTitleBackgroundQuestionsText_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.EditTitleBackgroundQuestionsText();
        }

        static void miEditTitleComment_Click(object sender, RoutedEventArgs e)
        {
            Commands.EditCommentTextCommand.EditCommentTextOfTitle();
        }

        static void miEditTitle_Click(object sender, RoutedEventArgs e)
        {

            Commands.EditTitleTextCommand.EditTitleTextOfSwitchCommentVisibleWhenFullScreen();
        }

        static void miInsertSameLevelTitle_Click(object sender, RoutedEventArgs e)
        {
            Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();
        }

        public Title(MainWindow editor)
        {
            Canvas.SetZIndex(this, 100);

            this.ContextMenu = contextMenu;

            this.mainBorder.BorderThickness = borderInitilizeThickness;
            this.mainBorder.MouseLeftButtonDown += new MouseButtonEventHandler(mainBorder_MouseLeftButtonDown);
            this.mainBorder.MouseRightButtonDown += new MouseButtonEventHandler(mainBorder_MouseRightButtonDown);
            this.mainBorder.PreviewMouseRightButtonUp += mainBorder_PreviewMouseRightButtonUp;

            this.MouseEnter += Title_MouseEnter;
            this.MouseLeave += Title_MouseLeave;

            this.mainTextBlock.Cursor = Cursors.Arrow;

            this.editor = editor;

            if (editor != null) this.isFillBlankVisible = (editor.ckxForceFillBlankView.IsChecked != true);

            this.RebuildANewID();

            imgStackPanel.VerticalAlignment = VerticalAlignment.Center;
            textStackPanel.VerticalAlignment = VerticalAlignment.Center;

            textStackPanel.Children.Add(mainTextBlock);
            textStackPanel.Children.Add(commentTextBlock);

            baseDockPanel.Children.Add(imgStackPanel);
            DockPanel.SetDock(imgStackPanel, Dock.Left);//默认图片在文本左侧。
            baseDockPanel.Children.Add(textStackPanel);
            mainBorder.Child = baseDockPanel;

            this.Child = mainBorder;

            mainTextBlock.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(textBlock_PreviewMouseLeftButtonDown);
            mainTextBlock.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(textBlock_MouseLeftButtonDown);
            mainTextBlock.MouseRightButtonDown += new MouseButtonEventHandler(textBlock_MouseRightButtonDown);

            commentTextBlock.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(commentTextBlock_PreviewMouseLeftButtonDown);
            commentTextBlock.MouseLeftButtonDown += new MouseButtonEventHandler(commentTextBlock_MouseLeftButtonDown);
            commentTextBlock.MouseRightButtonDown += new MouseButtonEventHandler(commentTextBlock_MouseRightButtonDown);

            mainTextBlock.MouseLeftButtonUp += new MouseButtonEventHandler(mainTextBlock_MouseLeftButtonUp);
            commentTextBlock.MouseLeftButtonUp += new MouseButtonEventHandler(commentTextBlock_MouseLeftButtonUp);

            //添加装饰器（选取框）
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.mainBorder);
            selectedAdorner = new TitleSelectedAdorner(this.mainBorder, this);// { Visibility = Visibility.Hidden };
            selectedAdorner.MouseLeftButtonDown += selectedAdorner_MouseLeftButtonDown;
            adornerLayer.Add(selectedAdorner);

            questionTooltipAdorner = new TitleQuestionTooltipAdorner(this.mainBorder, this);
            questionTooltipAdorner.MouseLeftButtonDown += questionTooltipAdorner_MouseLeftButtonDown;
            adornerLayer.Add(questionTooltipAdorner);

            this.commentAdorner = new CommentAdorner(this, this.commentTextBlock) { Visibility = Visibility.Hidden };
            commentAdorner.MouseLeftButtonUp += new MouseButtonEventHandler(commentAdorner_MouseLeftButtonUp);
            adornerLayer.Add(commentAdorner);

            this.collapseAdorner = new TitleCollapseAdorner(this.mainBorder, this) { Visibility = Visibility.Hidden };
            this.collapseAdorner.MouseLeftButtonDown += new MouseButtonEventHandler(collapseAdorner_MouseLeftButtonDown);
            adornerLayer.Add(collapseAdorner);

            this.addSameLevelTitleAdorner = new AddSameLevelTitleAdorner(this.mainBorder, this);
            this.addSameLevelTitleAdorner.MouseLeftButtonDown += addSameLevelTitleAdorner_MouseLeftButtonDown;
            adornerLayer.Add(addSameLevelTitleAdorner);

            this.addLowerLevelTitleAdorner = new AddLowerLevelTitleAdorner(this.mainBorder, this);
            this.addLowerLevelTitleAdorner.MouseLeftButtonDown += addLowerLevelTitleAdorner_MouseLeftButtonDown;
            adornerLayer.Add(addLowerLevelTitleAdorner);

            this.mainBorder.MouseLeave += new MouseEventHandler(mainBorder_MouseLeave);
            this.mainBorder.MouseUp += new MouseButtonEventHandler(mainBorder_MouseUp);
        }

        void mainBorder_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.ContextMenu.IsOpen = true;

            //e.Handled = true;
        }

        void questionTooltipAdorner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.PresentateQuestions();
        }

        void Title_MouseEnter(object sender, MouseEventArgs e)
        {
            if (this.editor == null) return;

            this.Editor.RefreshMousePointToTitle(this);
        }

        void Title_MouseLeave(object sender, MouseEventArgs e)
        {
            Globals.MainWindow.mousePointer.Visibility = System.Windows.Visibility.Hidden;
        }

        void selectedAdorner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            mainBorder_MouseLeftButtonDown(sender, e);
        }

        void addLowerLevelTitleAdorner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectOnlySelf();

            Commands.InsertLowerLevelTitleCommand.InsertLowerLevelTitle();
            e.Handled = true;
        }

        void addSameLevelTitleAdorner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectOnlySelf();

            Commands.InsertSameLevelTitleCommand.InsertSameLevelTitle();
            e.Handled = true;
        }

        void collapseAdorner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectOnlySelf();

            SwitchTitleCollapsedCommand.SwitchTitleCollapsed();
            this.collapseAdorner.InvalidateVisual();
            e.Handled = true;
        }

        #region 废弃此属性，改为直接显示填空答案。
        //private bool isCommentVisible = true;

        ///// <summary>
        ///// 这个属性不会写入文档。也不会与撤销列表相关，只有在全屏时才会有效。
        ///// </summary>
        //public bool IsCommentVisible
        //{
        //    get { return isCommentVisible; }
        //    set
        //    {
        //        isCommentVisible = value;

        //        RefreshCommentTextVisible();

        //        RefreshNeedSize(false);
        //    }
        //} 
        #endregion

        private bool isFillBlankVisible = true;

        public bool IsFillBlankVisible
        {
            get { return isFillBlankVisible; }
            set
            {
                isFillBlankVisible = value;

                RefreshFillBlankVisible();

                RefreshAutoNumberColors();
                //RefreshNeedSize(false);//不需要重新计算尺寸。
            }
        }

        private void RefreshFillBlankVisible()
        {
            if (editor == null) return;

            if (editor.tbtnFullScreen.IsChecked != true && editor.ckxForceFillBlankView.IsChecked != true)
            {
                foreach (var item in this.mainTextBlock.Inlines)
                {
                    var span = item as Span;
                    if (span == null || span == this.autoNumberSpan) continue;

                    if (span.Tag != null && span.Tag.ToString() == "FillBlank")
                    {
                        foreach (var item2 in span.Inlines)
                        {
                            var run = item2 as Run;
                            if (run == null) continue;

                            if (this.isFillBlankVisible)
                            {
                                run.Foreground = Brushes.Red; // 2021年6月3日 填空下划线总是红色的，所以填空项也是红色的好。
                                //ColorPicker.GetBrushByName(run.Tag as string); //非全屏状态下，总是如实显示。
                            }
                            else
                            {
                                run.Foreground = Brushes.Transparent;
                            }
                        }
                    }
                }
                return;//本来仅全屏状态下才有效果。2014年1月28日在主界面添加“强制呈现为填空模式”选择框。
            }

            //全屏状态下可以开关填空模式。
            foreach (var item in this.mainTextBlock.Inlines)
            {
                var span = item as Span;
                if (span == null || span == this.autoNumberSpan) continue;

                if (span.Tag != null && span.Tag.ToString() == "FillBlank")
                {
                    foreach (var item2 in span.Inlines)
                    {
                        var run = item2 as Run;
                        if (run == null) continue;

                        if (this.isFillBlankVisible)
                        {
                            //run.Foreground = ColorPicker.GetBrushByName(run.Tag as string);
                            run.Foreground = Brushes.Red;
                        }
                        else
                        {
                            run.Foreground = Brushes.Transparent;
                        }
                    }
                }
            }
        }

        void commentAdorner_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            DropOrLinkTitle(); e.Handled = true;
        }

        void commentTextBlock_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            DropOrLinkTitle(); e.Handled = true;
        }

        void mainTextBlock_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            DropOrLinkTitle(); e.Handled = true;
        }

        internal void DropOrLinkTitle()
        {
            if (editor == null) return;

            if (editor.isPrepareInsertCommentOrLinkTitle)
            {
                editor.isPrepareInsertCommentOrLinkTitle = false;

                Title mainSelectedTitle = null;

                if (Globals.MainWindow.SelectedTitlesList.Count <= 0)
                {
                    if (Globals.MainWindow.SelectedTitleImage == null) return;

                    mainSelectedTitle = Globals.MainWindow.SelectedTitleImage.MasterTitle;
                }

                foreach (Title t in Globals.SelectedTitlesList)
                {
                    if (t.IsMainSelectedTitle)
                    {
                        mainSelectedTitle = t; break;
                    }
                }

                if (mainSelectedTitle == null) return;
                if (mainSelectedTitle.IsFixed) return;
                if (mainSelectedTitle == this) return;//不能给自己添加关系线。

                Commands.AddRelationCommand.AddRelation(mainSelectedTitle, this);
            }
            else if (editor.isDraggingTitle)
            {
                editor.isDraggingTitle = false;

                if (Globals.SelectedTitlesList.Count <= 0) return;

                //准备拖动。
                Title mainTitle = null;
                foreach (Title t in Globals.SelectedTitlesList)
                {
                    if (t.IsMainSelectedTitle)
                    {
                        mainTitle = t; break;
                    }
                }

                if (mainTitle == null) return;

                if (mainTitle == this)
                {
                    RefreshEditorTitleDraggingHandler();
                    return;
                }

                //如果源标题是目标的上级。
                Title superTitle = mainTitle.GetHigherLvelTitle();
                if (superTitle != null && superTitle == this)
                {
                    mainTitle.RefreshEditorTitleDraggingHandler();
                    MessageBox.Show("源标题已经是目标标题的直接下级！", Globals.AppName,
                        MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;//源标题是本标题的直接下级，没必要拖动。
                }

                superTitle = this.GetHigherLvelTitle();
                while (superTitle != null)
                {
                    if (superTitle == mainTitle)
                    {
                        mainTitle.RefreshEditorTitleDraggingHandler();
                        MessageBox.Show("不允许将一个标题拖动到自身的下级！", Globals.AppName,
                            MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }

                    superTitle = superTitle.GetHigherLvelTitle();
                }

                //如果符合要求。

                superTitle = mainTitle.GetHigherLvelTitle();
                if (superTitle == null) return;

                List<Title> oldBrothers = superTitle.GetLowerLevelTitles();
                List<Title> newBrothers = this.GetLowerLevelTitles();

                if (oldBrothers == null || oldBrothers.Contains(mainTitle) == false) return;

                ModifingItem mi = new ModifingItem()
                {
                    ModifingName = "拖动到另一标题之下",
                    IsNeedRefreshTitleAndLinesLocation = true,
                };

                //TODO 拖动。
                Action act = new Action(mainTitle.ID, -1, mainTitle.GetType().Name, "HigherLevelTitleID",
                    mainTitle.HigherLevelTitleID, this.ID);

                if (mainTitle.Direction != this.Direction)
                {
                    Action actDirection = new Action(mainTitle.ID, -1, mainTitle.GetType().Name, "Direction",
                        mainTitle.Direction.ToString(), this.Direction.ToString());

                    mainTitle.Direction = this.Direction;

                    mi.Add(actDirection);
                }

                int oldIndex = mainTitle.Index;
                int newIndex = newBrothers.Count;

                if (mainTitle.Index != newIndex)//有可能碰巧索引一致。
                {
                    Action actIndex = new Action(mainTitle.ID, -1, mainTitle.GetType().Name, "Index",
                        mainTitle.Index.ToString(), newIndex.ToString());

                    mainTitle.Index = newIndex;

                    mi.Add(actIndex);
                }

                mainTitle.HigherLevelTitleID = this.ID;//这个属性也会改变Direction.

                mi.Add(act);

                foreach (Title t in oldBrothers)
                {
                    if (t == mainTitle) continue;

                    if (t.Index > oldIndex)
                    {
                        Action actIndex = new Action(t.ID, -1, t.GetType().Name, "ID", t.Index.ToString(),
                             (t.Index - 1).ToString());

                        t.Index -= 1;

                        mi.Add(actIndex);
                    }
                }

                editor.Do(mi);
                editor.IsModified = true;
                editor.RefreshTitleAndLinesLocation();

                //这两种情况要刷新编号的。
                if (editor.View == ViewMode.HorizontalList || editor.View == ViewMode.VerticalList)
                {
                    editor.RefreshAutoNumber();
                }
            }
            else
            {
                if (Globals.MainWindow.isDraggingFixedTitle)
                {
                    if (Globals.MainWindow.MainSelectedTitle != null)
                    {
                        FixedTitle ft = Globals.MainWindow.MainSelectedTitle as FixedTitle;
                        if (ft != null)
                        {
                            ft.RefreshLoaction();
                            ft.SelectOnlySelf();
                        }
                    }

                    Globals.MainWindow.isDraggingFixedTitle = false;
                }
            }
        }

        private int globalIndex = -1;

        /// <summary>
        /// 用于PageDown找下一个。
        /// </summary>
        public int GlobalIndex
        {
            get { return this.globalIndex; }
            set { this.globalIndex = value; }
        }

        private Comment summaryComment = null;

        public Comment SummaryComment
        {
            get
            {
                if (editor == null) return null;

                if (editor.mainCanvas.Children.Contains(summaryComment)) summaryComment = null;//如果已被删除！

                if (summaryComment != null) return summaryComment;

                foreach (UIElement ue in editor.mainCanvas.Children)
                {
                    Comment c = ue as Comment;
                    if (c == null) continue;

                    if (c.MasterTitleID == this.ID && c.IsSummary)
                    {
                        summaryComment = c;
                        return c;
                    }
                }
                return null;
            }
        }

        public void SetSummaryCommentToNull()
        {
            summaryComment = null;
        }

        private int indent = 0;

        /// <summary>
        /// 缩进级别。
        /// </summary>
        public int Indent
        {
            get { return indent; }
            set
            {
                indent = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("Indent", value.ToString());
                }

                //这个交由全局布局方法去更新。
            }
        }

        public double IndentWidth
        {
            get
            {
                if (Globals.MainWindow == null) return 0;

                //return Globals.MainWindow.FontSize * 2 * indent;
                return Globals.MainWindow.DefaultFontSize * 2 * indent;
            }
        }

        private string questionText = "";

        public string QuestionText
        {
            get { return this.questionText; }
            set
            {
                this.questionText = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("QuestionText", value);
                }

                this.RefreshQuestionText();
            }
        }

        private void RefreshQuestionText()
        {
            this.questionTooltipAdorner.InvalidateVisual();
        }

        void mainBorder_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectOnlySelf();
            e.Handled = true;
        }

        void commentTextBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectOnlySelf();
            e.Handled = true;
        }

        void textBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectOnlySelf();
            e.Handled = true;
        }

        void mainBorder_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Globals.MainWindow.isPrepareInsertCommentOrLinkTitle = false;
            Globals.MainWindow.isPrepareInsertPictureBox = false;
            Globals.MainWindow.isPrepareInsertFixedTitle = false;
            e.Handled = true;
        }

        void mainBorder_MouseLeave(object sender, MouseEventArgs e)
        {
            if (editor != null && editor.isDraggingTitle)
            {
                return;
            }

            bool isShift = false;

            KeyStates lShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates rShift = Keyboard.GetKeyStates(Key.RightShift);

            if ((lShift & KeyStates.Down) > 0 || (rShift & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            bool isAlt = false;

            KeyStates lAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates rAlt = Keyboard.GetKeyStates(Key.RightAlt);

            if ((lAlt & KeyStates.Down) > 0 || (rAlt & KeyStates.Down) > 0)
            {
                isAlt = true;
            }

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (isShift)
                {
                    Globals.MainWindow.isPrepareInsertPictureBox = true;
                }
                else
                {
                    if (isAlt)
                    {
                        Globals.MainWindow.isPrepareInsertFixedTitle = true;
                    }
                    else
                    {
                        Globals.MainWindow.isPrepareInsertCommentOrLinkTitle = true;
                    }
                }

                e.Handled = true;
            }
        }

        void textBlock_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //编辑文本
            if (e.ClickCount == 2)
            {
                Select();

                EditTitleTextCommand.EditTitleTextOfSwitchCommentVisibleWhenFullScreen();

                e.Handled = true;
            }
        }

        void commentTextBlock_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //编辑文本
            if (e.ClickCount == 2)
            {
                Select();

                EditCommentTextCommand.EditCommentTextOfTitle();

                e.Handled = true;
            }
        }

        void mainBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Select();

            e.Handled = true;//否则还会继续传递到Canvas.MouseLeftButtonDown事件，这会造成选取被取消。
        }

        void textBlock_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            Select();

            e.Handled = true;//否则还会继续传递到Canvas.MouseLeftButtonDown事件，这会造成选取被取消。
        }

        void commentTextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Select();

            e.Handled = true;//否则还会继续传递到Canvas.MouseLeftButtonDown事件，这会造成选取被取消。
        }

        /// <summary>
        /// 选取。这个比SelectOnlySelf()要复杂——要处理Ctrl键被按住时的多选问题。
        /// </summary>
        private void Select()
        {
            if (Globals.MainWindow.SelectedTitleImage != null) Globals.MainWindow.SelectedTitleImage = null;
            if (Globals.MainWindow.SelectedRelation != null) Globals.MainWindow.SelectedRelation = null;
            if (Globals.MainWindow.SelectedComment != null) Globals.MainWindow.SelectedComment = null;
            if (Globals.MainWindow.SelectedPictureBox != null) Globals.MainWindow.SelectedPictureBox = null;

            bool isLeftCtrlDown = System.Windows.Input.Keyboard.IsKeyDown(Key.LeftCtrl);
            bool isRightCtrlDown = System.Windows.Input.Keyboard.IsKeyDown(Key.RightCtrl);

            if (isLeftCtrlDown || isRightCtrlDown)
            {
                foreach (Title t in editor.SelectedTitlesList)
                {
                    if (t.IsMainSelectedTitle)
                    {
                        t.IsMainSelectedTitle = false;
                    }
                }

                if (editor.SelectedTitlesList.Contains(this))
                {
                    editor.SelectedTitlesList.Remove(this);
                    this.IsSelected = false;
                    this.IsMainSelectedTitle = false;

                    if (editor.SelectedTitlesList.Count > 0)
                    {
                        editor.SelectedTitlesList[0].IsMainSelectedTitle = true;
                    }
                }
                else
                {
                    editor.SelectedTitlesList.Add(this);
                    this.IsSelected = true;
                    this.IsMainSelectedTitle = true;
                }
            }
            else
            {
                if (editor.SelectedTitlesList.Contains(this))
                {
                    //直接点击，不清空现在选取的所有Title。仅改变当前主选取标题。
                    this.IsSelected = true;//这行得用属性。

                    foreach (Title t in Globals.SelectedTitlesList)
                    {
                        if (t.IsMainSelectedTitle)
                        {
                            t.IsMainSelectedTitle = false;
                        }
                    }

                    this.IsMainSelectedTitle = true;//避免重复刷新SelectedAdorner.因此这行用字段。
                }
                else
                {
                    //如果此标题不在被选取的标题列表中，则清除现有列表。
                    editor.ClearAllFromSelectedTitleList();

                    editor.SelectedTitlesList.Add(this);
                    this.IsSelected = true;
                    this.IsMainSelectedTitle = true;
                }
            }
        }

        /// <summary>
        /// 使编辑器仅选中自身。如果不可见，会自动尝试滚动编辑区。
        /// </summary>
        public void SelectOnlySelf()
        {
            if (editor == null) return;

            if (editor.SelectedTitleImage != null) editor.SelectedTitleImage = null;
            if (editor.SelectedRelation != null) editor.SelectedRelation = null;
            if (editor.SelectedComment != null) editor.SelectedComment = null;
            if (editor.SelectedPictureBox != null) editor.SelectedPictureBox = null;

            editor.ClearAllFromSelectedTitleList();

            editor.SelectedTitlesList.Add(this);
            this.IsSelected = true;
            this.IsMainSelectedTitle = true;

            //Title.TryToShowTitleInVisualArea(this, true);
            //若是这时调用，则会导致卷动动画失效。——因为MainWindow.TryScrollTitle会判断Title是否已在可视区，
            //而如果调用这个方法，那么，title将总是可视的。

            this.Editor.TryScrollTitle(this);
        }

        private static Thickness mainBorderMargin = new Thickness(10);

        private static Thickness mainBorderPadding = new Thickness(20);

        private static Thickness borderInitilizeThickness = new Thickness(1);

        private Brush relationLineStroke = Globals.RandomBrush;

        /// <summary>
        /// 只有当标题是SubRoot[一级标题]时，才有意义。
        /// 另，关系线分自动绘制关系线（用户不能修改，最多自定义其色彩）外，
        /// 还有一种是用户自定义的关系线（由贝塞尔曲线构成）。
        /// </summary>
        public Brush RelationLineStroke
        {
            get { return relationLineStroke; }
            set
            {
                relationLineStroke = value;

                if (autoRelationPath != null/* && lowerAutoRelationPath != null*/)
                {
                    autoRelationPath.Stroke = autoRelationPath.Fill = value;
                    //lowerAutoRelationPath.Stroke = value;
                }

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("RelationLineStroke", ColorPicker.GetBrushName(value));
                }
            }
        }

        private Path autoRelationPath = null;

        /// <summary>
        /// 自动绘制的关系线。为了便于重新指定分支线的色彩才提供此属性。
        /// 此属性只对[一级标题,即SubRoot]有意义。
        /// </summary>
        public Path AutoRelationPath
        {
            get { return autoRelationPath; }
            set { autoRelationPath = value; }
        }

        //private Path lowerAutoRelationPath = null;

        ///// <summary>
        ///// 自动绘制的关系线。为了便于重新指定分支线的色彩才提供此属性。
        ///// 此属性只对[一级标题,即SubRoot]有意义。
        ///// </summary>
        //public Path LowerAutoRelationPath
        //{
        //    get { return lowerAutoRelationPath; }
        //    set { lowerAutoRelationPath = value; }
        //}

        private TitleDirection direction = TitleDirection.Right;

        /// <summary>
        /// ★此属性的取值可能与后台Xml数据不一致！！
        /// 
        /// 此属性仅只针对根标题的直接下级（也就是一级标题）有效，对根标题无意义，对其它标题也无意义。
        /// 根标题总是尽可能靠近Canvas的中央（但不一定在中央）。
        /// 一级标题将决定其下所有标题（如二级、三级直到理论上的无穷级）的指向。
        /// 
        /// 如果此标题不是根标题，也不是一级标题。则在布局时，会将direction字段值按一级标题的Direction来设。
        /// ——不会管其后台Xml文档中的Direction特性的值是什么。
        /// </summary>
        public TitleDirection Direction
        {
            get { return direction; }
            set
            {
                direction = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("Direction", value.ToString());
                }

                if (isSelected && isMainSelectedTitle)
                {
                    RefreshEditorTitleDraggingHandler();
                }
            }
        }

        /// <summary>
        /// 此方法只应在重排所有标题时(LocateAtLeft()方法或LocateAtRight()方法中)调用。
        /// </summary>
        private void ResetDirection(TitleDirection newDirection)
        {
            direction = newDirection;

            RefreshDirection();
        }

        public void RefreshDirection()
        {
            if (Globals.MainWindow.View != ViewMode.Default || direction == TitleDirection.Right)//只有导图才需要分左右
            {
                mainTextBlock.HorizontalAlignment = HorizontalAlignment.Left;
                if (this.commentTextBlock != null)
                {
                    this.commentTextBlock.HorizontalAlignment = HorizontalAlignment.Left;
                    var marginLeft = mainTextBlock.FontSize * 2;
                    this.commentTextBlock.Margin = new Thickness(marginLeft, 0, 0, 0);
                    this.commentTextBlock.MaxWidth = 400 - marginLeft;
                }
            }
            else
            {
                mainTextBlock.HorizontalAlignment = HorizontalAlignment.Right;
                if (this.commentTextBlock != null)
                {
                    this.commentTextBlock.HorizontalAlignment = HorizontalAlignment.Right;
                    var marginRight = mainTextBlock.FontSize * 2;
                    this.commentTextBlock.Margin = new Thickness(0, 0, marginRight, 0);
                    this.commentTextBlock.MaxWidth = 400 - marginRight;
                }
            }

            if (this.commentAdorner != null)
            {
                this.commentAdorner.InvalidateVisual();
            }

            RefreshTextBlockAlignment();
        }

        private Border mainBorder = new Border()
        {
            Margin = mainBorderMargin,
            CornerRadius = new CornerRadius(5),
        };

        public Border MainBorder
        {
            get { return mainBorder; }
        }

        /// <summary>
        /// 是否存在填空项。（其实就是带下划线的字符串；不考虑备注文本区域。）
        /// </summary>
        public bool IsFillBlankExist
        {
            get { return isUnderLineExist; }
        }

        private bool isUnderLineExist = false;

        /// <summary>
        /// 是否存在带下划线的字符串。（不考虑备注文本区域。）
        /// </summary>
        public bool IsUnderLineExist
        {
            get { return isUnderLineExist; }
        }

        private Size needSize = new Size();

        /// <summary>
        /// [只读]除根标题外，此属性的值只能由RefreshNeedSize更改。
        /// </summary>
        public Size NeedSize
        {
            get { return needSize; }
        }

        public void SetNeedSize(double newWidth, double newHeight)
        {
            if (this.higherLevelTitleID == null)
            {
                needSize.Width = newWidth;
                needSize.Height = newHeight;
            }
            else
            {
                MessageBox.Show("此标题存在上级标题ID，理论上不应该是根标题。不允许使用“SetNeedSize()”方法。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
        }

        private DockPanel baseDockPanel = new DockPanel() { LastChildFill = false };

        /// <summary>
        /// 在baseStackPanel内部左侧。
        /// </summary>
        private StackPanel imgStackPanel = new StackPanel() { Orientation = Orientation.Horizontal };

        public StackPanel ImgStackPanel
        {
            get { return imgStackPanel; }
        }

        /// <summary>
        /// 在baseStackPanel内部右侧。
        /// </summary>
        private StackPanel textStackPanel = new StackPanel() { Orientation = Orientation.Vertical };

        private TextBlock mainTextBlock = new TextBlock()
        {
            Foreground = Brushes.Black,
            TextWrapping = TextWrapping.Wrap,
            MaxWidth = 400,//设置最大宽度为400，以便文本自动折行。2014年12月23日
            //最大宽度为什么是400呢？这是考虑到投影的需要
            //在8米长度的教室中，我近视眼可以看到150%倍放大的字，但已经很吃力。
            //而100%未放大的显示比例下，根本看不到。
            //而200%虽然有些浪费，但我能看见，后排学生一般也不成问题。
            //普通投影仪4：3的长宽比，一般均支持1024×768的分辨率，
            //宽度为500时，如果标题跨层级，在两种列表视图下，
            //不是上级横向显示不全，就是下级横向显示不全。
            //所以400是折衷后的数值。
        };

        private static string textBlockToolTip =
"[Enter|双击]　　　　编辑文本\r\n" +
"[左键拖出]　　　　　添加备注文本块\r\n" +
"[Alt+左键拖出]　　　添加自适应标题\r\n" +
"[Shift+左键拖出]　　添加图片框\r\n" +
"[Ctrl+数字]　　　 　设置缩进值\r\n" +
"[拖动到另一个标题]　画连接线";

        public static string TextBlockToolTip
        {
            get { return textBlockToolTip; }
        }

        private int borderWidth = 1;

        /// <summary>
        /// 默认值：1。
        /// </summary>
        public int BorderWidth
        {
            get { return borderWidth; }
            set
            {
                borderWidth = value;
                if (xmlData != null)
                {
                    this.XmlData.SetAttribute("BorderWidth", borderWidth.ToString());
                }

                this.mainBorder.BorderThickness = new Thickness(borderWidth);
                this.Editor.IsModified = true;
            }
        }

        /// <summary>
        /// 保留这个字段目的是用在IsSelected属性改变时恢复边框色。
        /// </summary>
        protected Brush borderColor = Brushes.Transparent;

        /// <summary>
        /// 自定义边框色属性。对原有的BorderBrush进行了封装。
        /// 这个值并不一定等于MainBorder的BorderBrush，因为有些公式
        /// （如网格式）会有“自动边框色”。
        /// </summary>
        public Brush BorderColor
        {
            get { return borderColor; }
            set
            {
                this.borderColor = value;

                if (value == null)
                {
                    this.mainBorder.BorderBrush = Brushes.Transparent;
                }
                else
                {
                    this.mainBorder.BorderBrush = value;
                }

                this.XmlData.SetAttribute("BorderColor", ColorPicker.GetBrushName(value));
                Editor.IsModified = true;
            }
        }

        public TextBlock MainTextBlock
        {
            get { return mainTextBlock; }
        }

        //注释文本块默认不显示。只有当存在注释文本时，才显示。
        private TextBlock commentTextBlock = new TextBlock()
        {
            Visibility = Visibility.Collapsed,
            //Opacity = 0.75,
            Foreground = Brushes.Green,
            ToolTip = "[Ctrl+Enter|双击]编辑备注文本",
            TextWrapping = TextWrapping.Wrap,
            //MaxWidth = 364,//要考虑箭头空间，所以不能满400，但若要保持对齐，就不能在这晨设置。
        };

        /// <summary>
        /// 注释文本块。
        /// </summary>
        public TextBlock CommentTextBlock
        {
            get { return commentTextBlock; }
        }

        /// <summary>
        /// 注释文本块的装饰器。一个箭头。
        /// </summary>
        private Adorners.CommentAdorner commentAdorner;

        #region 已不需要此属性，2011年11月20日
        //protected string commentText = null;

        //public virtual string CommentText
        //{
        //    get { return commentText; }
        //    set
        //    {
        //        if (commentText == value) return;

        //        commentText = value;
        //        this.commentTextBlock.Text = value;
        //        if (xmlData != null)
        //        {
        //            xmlData.SetAttribute("CommentText", commentText);
        //        }

        //        OnPropertyChanged(this, new PropertyChangedEventArgs("CommentText"));
        //        RefreshCommenttextVisible();
        //    }
        //} 
        #endregion

        public void RefreshCommentTextVisible()
        {
            RefreshDirection();

            //Comment不再用作显示填空题答案。//2013年10月6日
            //if (Editor != null)
            //{
            //    if (editor.tbtnFullScreen.IsChecked == true)
            //    {
            //        if (isCommentVisible == false)
            //        {
            //            commentTextBlock.Visibility = System.Windows.Visibility.Collapsed;
            //            commentAdorner.Visibility = System.Windows.Visibility.Hidden;
            //            return;
            //        }
            //    }
            //}

            XmlNode commentSetNode = this.XmlData.SelectSingleNode("CommentSet");
            if (commentSetNode == null || commentSetNode.ChildNodes.Count <= 0)
            {
                commentTextBlock.Visibility = Visibility.Collapsed;
                commentAdorner.Visibility = Visibility.Hidden;
                return;
            }

            if (commentSetNode.FirstChild.InnerText.Length <= 0)
            {
                commentTextBlock.Visibility = Visibility.Collapsed;
                commentAdorner.Visibility = Visibility.Hidden;
                return;
            }

            if (commentTextBlock.Text == null || commentTextBlock.Text.Length <= 0)
            {
                commentTextBlock.Visibility = Visibility.Collapsed;
                commentAdorner.Visibility = Visibility.Hidden;
            }
            else
            {
                commentTextBlock.Visibility = Visibility.Visible;
                commentAdorner.Visibility = Visibility.Visible;
            }
        }

        private Brush titleBackground = Brushes.Transparent;

        public Brush TitleBackground
        {
            get { return titleBackground; }
            set
            {
                titleBackground = value;

                Editor.IsModified = true;

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("TitleBackground", ColorPicker.GetBrushName(titleBackground));
                }

                RefreshTitleBackground();
                OnPropertyChanged(this, new PropertyChangedEventArgs("TitleBackground"));
            }
        }

        public void RefreshTitleBackground()
        {
            XmlAttribute attrBackground = xmlData.GetAttribute("TitleBackground");
            if (attrBackground != null)
            {
                var newBrush = ColorPicker.GetBrushByName(attrBackground.Value);
                if (newBrush == Brushes.Transparent)
                {
                    if (this.IsRootTitle)
                    {
                        this.titleBackground = mainBorder.Background = Globals.MainWindow.mainCanvas.Background;
                        return;
                    }
                }
                this.titleBackground = mainBorder.Background = newBrush;
                OnPropertyChanged(this, new PropertyChangedEventArgs("TitleBackground"));
            }
            else
            {
                if (IsRootTitle)
                {
                    this.titleBackground = mainBorder.Background = editor.mainCanvas.Background;
                    //Brushes.White;//根标题默认白色背景
                    //二○一四年十二月三十日11时10分改为：如果不设置，则根标题的背景色与文档背景色相同。
                }
                else
                {
                    this.titleBackground = mainBorder.Background = Brushes.Transparent;
                }
            }
        }

        private string iconXamlText = string.Empty;
        /// <summary>
        /// [读写]用于存储Xaml文本，此文本用于自动载入为新图标。
        /// </summary>
        public string IconXamlText
        {
            get { return iconXamlText; }
            set
            {
                this.iconXamlText = value;
                if (this.xmlData != null)
                {
                    XmlNode iconXamlNode = this.xmlData.SelectSingleNode("IconXamlText");
                    if (iconXamlNode == null)
                    {
                        this.xmlData.AppendXml("<IconXamlText>" + value + "</IconXamlText>");
                    }
                    else
                    {
                        iconXamlNode.InnerXml = value;
                    }
                }

                this.RefreshIconXamlText();
            }
        }

        /// <summary>
        /// 由XamlReader.Load()方法生成的FrameworkElement。
        /// </summary>
        private FrameworkElement xamlIconFrameWorkElement = null;

        /// <summary>
        /// 刷新IconXamlText(载入图标）。
        /// </summary>
        /// <returns>如果正常执行，返回string.Empty。否则返回异常信息。</returns>
        private void RefreshIconXamlText()
        {
            if (this.textStackPanel.Children.Contains(xamlIconFrameWorkElement))
            {
                this.textStackPanel.Children.Remove(xamlIconFrameWorkElement);
                xamlIconFrameWorkElement = null;
            }

            if (this.iconXamlText != null && this.iconXamlText.Length > 0)
            {
                if (iconXamlText.StartsWith("<LunarConcept>") && iconXamlText.EndsWith("</LunarConcept>"))
                {
                    TryLoadConcept();
                    return;
                }
                else
                {
                    byte[] array = System.Text.Encoding.UTF8.GetBytes(this.iconXamlText.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&amp;lt;", "<").Replace("&amp;gt;", ">").Replace("<Viewbox x:Shared=\"false\"", "<Viewbox"));

                    try
                    {
                        using (var stream = new System.IO.MemoryStream(array))
                        {
                            this.xamlIconFrameWorkElement = System.Windows.Markup.XamlReader.Load(stream) as FrameworkElement;

                            if (this.textStackPanel.Children.Contains(this.xamlIconFrameWorkElement) == false)
                            {
                                this.textStackPanel.Children.Add(this.xamlIconFrameWorkElement);
                            }

                            this.xamlIconFrameWorkElement.SizeChanged += xamlIconFrameWorkElement_SizeChanged;
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }
        }

        private void TryLoadConcept()
        {
            if (this.xamlIconFrameWorkElement != null &&
                this.textStackPanel.Children.Contains(this.xamlIconFrameWorkElement))
            {
                this.textStackPanel.Children.Remove(this.xamlIconFrameWorkElement);
                this.xamlIconFrameWorkElement = null;
            }

            PageEditorReader per = new PageEditorReader();
            this.xamlIconFrameWorkElement = per;

            var xamlTextNode = this.xmlData.SelectSingleNode("IconXamlText");
            if (xamlTextNode != null)
            {
                var conceptNode = xamlTextNode.SelectSingleNode(XmlTags.LunarConceptTag);
                per.XmlData = conceptNode.SelectSingleNode(XmlTags.PageTag);
                //per.Build();//设置XmlData属性值时会自动调用。

                this.textStackPanel.Children.Add(this.xamlIconFrameWorkElement);
            }
        }

        void xamlIconFrameWorkElement_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (this.editor != null)
                this.editor.RefreshTitleAndLinesLocation();
        }

        protected Brush titleForeground = Brushes.Black;

        public virtual Brush TitleForeground
        {
            get { return titleForeground; }
            set
            {
                titleForeground = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("TitleForeground", ColorPicker.GetBrushName(titleForeground));
                }

                mainTextBlock.Foreground = value;
                commentTextBlock.Foreground = value;

                if (commentAdorner != null && commentAdorner.Visibility == Visibility.Visible)
                {
                    commentAdorner.InvalidateVisual();
                }

                OnPropertyChanged(this, new PropertyChangedEventArgs("TitleForeground"));
            }
        }

        private XmlNode xmlData;

        public XmlNode XmlData
        {
            get { return xmlData; }
            set
            {
                xmlData = value;
                BuildTitle();

                if (xmlData == null)
                {
                    commentTextBlock.Text = "";
                    //RefreshCommentTextVisible();//2013年10月6日废弃，改令标题直接支持FillBlank。
                    RefreshFillBlankVisible();
                }
            }
        }

        /// <summary>
        /// 相对于直接设置XmlData属性值，只少一步：调用构建方法BuildTitle()。
        /// </summary>
        /// <param name="newXmlData"></param>
        public void SetXmlData(XmlNode newXmlData)
        {
            this.xmlData = newXmlData;
            if (xmlData == null)
            {
                commentTextBlock.Text = "";
                //RefreshCommentTextVisible();
                RefreshFillBlankVisible();
            }
        }

        private string id = null;

        /// <summary>
        /// GUID字符串。
        /// </summary>
        public string ID
        {
            get { return id; }
        }

        public void RebuildANewID()
        {
            id = Guid.NewGuid().ToString();
            if (this.xmlData != null)
            {
                this.xmlData.SetAttribute("ID", id);
            }
        }

        /// <summary>
        /// 根据是否存在“higherLevelTitleID”来判断是否存在上级。
        /// 如果不存在，则是根标题。
        /// </summary>
        public bool IsRootTitle
        {
            get
            {
                if (higherLevelTitleID == null || higherLevelTitleID.Length <= 0)
                {
                    return true;
                }
                else return false;
            }
        }

        private string higherLevelTitleID = null;

        /// <summary>
        /// 根标题此属性值总是为null。
        /// </summary>
        public string HigherLevelTitleID
        {
            get { return higherLevelTitleID; }
            set
            {
                if (higherLevelTitleID != value)
                {
                    higherLevelTitleID = value;
                    if (this.xmlData != null)
                    {
                        this.xmlData.SetAttribute("HigherLevelTitleID", value);
                    }

                    //editor.RefreshTitleAndLinesLocation();
                }
            }
        }

        /// <summary>
        /// 上级标题。如果是根标题，则此值只能是空。
        /// </summary>
        public Title GetHigherLvelTitle()
        {
            if (editor == null) return null;

            foreach (UIElement ue in editor.mainCanvas.Children)
            {
                Title t = ue as Title;
                if (t == null) continue;

                if (t.ID == this.higherLevelTitleID)
                {
                    return t;
                }
            }

            return null;
        }

        public Title GetSubRoot()
        {
            if (string.IsNullOrEmpty(this.id)) return null;
            if (editor == null || editor.RootTitle == null) return null;
            if (this.higherLevelTitleID == editor.RootTitle.id) return this;

            var rootTitle = editor.RootTitle;

            var parentTitle = this.GetHigherLvelTitle();
            while (parentTitle != null)
            {
                if (parentTitle.HigherLevelTitleID == rootTitle.ID) return parentTitle;

                parentTitle = parentTitle.GetHigherLvelTitle();
            }

            return null;
        }

        private List<Title> lowerLevelTitles = new List<Title>();

        /// <summary>
        /// ★此中的值不一定准确。要使用这个属性，请先调用GetLowerLevelTitles()方法。
        /// </summary>
        public List<Title> LowerLevelTitles
        {
            get { return lowerLevelTitles; }
        }

        /// <summary>
        /// 遍历整个编辑器中所有标题（Title），取出当前标题的所有下级标题。
        /// FixedTitle不算下级，而是“附属”。
        /// </summary>
        /// <returns></returns>
        public List<Title> GetLowerLevelTitles()
        {
            lowerLevelTitles.Clear();
            if (editor == null) return lowerLevelTitles;

            foreach (UIElement ue in editor.mainCanvas.Children)
            {
                if (ue == this) continue;//跳过自身。

                Title t = ue as Title;
                if (t == null) continue;

                if (t.IsFixed) continue;

                if (t.higherLevelTitleID == this.ID)
                {
                    lowerLevelTitles.Add(t);
                }
            }

            //取出后，还需要按索引排序。
            lowerLevelTitles.Sort(titleIndexSortter);

            return lowerLevelTitles;
        }

        internal static TitleIndexCompare titleIndexSortter = new TitleIndexCompare();

        private MainWindow editor;

        public MainWindow Editor
        {
            get { return editor; }
            set { editor = value; }
        }

        /// <summary>
        /// 取出本标题内嵌的某个TitleImage。
        /// </summary>
        /// <param name="ID">要找的TitleImage的ID。</param>
        /// <returns></returns>
        public TitleImage GetTitleImageByID(string ID)
        {
            if (ID == null || ID.Length <= 0) return null;
            if (imgStackPanel.Children.Count <= 0) return null;

            foreach (UIElement ue in imgStackPanel.Children)
            {
                TitleImage ti = ue as TitleImage;
                if (ti == null) continue;

                if (ti.ID == ID) return ti;
            }

            return null;
        }

        #region 实现INotifyPropertyChanged接口成员

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(object sender,
            PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sender, e);
            }
        }

        #endregion

        /// <summary>
        /// 此方法用以从XmlData中取出各属性的值。
        /// </summary>
        public virtual void BuildTitle()
        {
            if (this.XmlData == null) return;

            if (this.mainTextBlock == null)
            {
                this.mainTextBlock = new TextBlock();
                this.Child = this.mainTextBlock;
            }

            try
            {
                XmlAttribute attrID = this.xmlData.GetAttribute("ID");
                if (attrID != null)
                {
                    this.id = attrID.Value;
                }
                else
                {
                    RebuildANewID();
                }

                XmlAttribute attrIsFixed = this.xmlData.GetAttribute("IsFixed");
                if (attrIsFixed != null)
                {
                    this.isFixed = bool.Parse(attrIsFixed.Value);
                }

                XmlAttribute attrTextDock = this.xmlData.GetAttribute("TextDock");
                if (attrTextDock != null)
                {
                    this.textDock = (Dock)Enum.Parse(typeof(Dock), attrTextDock.Value);
                    RefreshTextDock();
                }

                XmlAttribute attrIndex = this.xmlData.GetAttribute("Index");
                if (attrIndex != null)
                {
                    this.index = int.Parse(attrIndex.Value);
                }

                XmlAttribute attrIndent = this.xmlData.GetAttribute("Indent");
                if (attrIndent != null)
                {
                    this.indent = int.Parse(attrIndent.Value);
                }

                //只有根标题的直接下级才有意义，其它标题即使读出来也不起任何作用。
                XmlAttribute attrDirection = this.xmlData.GetAttribute("Direction");
                if (attrDirection != null)
                {
                    this.direction = (TitleDirection)Enum.Parse(typeof(TitleDirection), attrDirection.Value);
                }

                XmlAttribute attrHigherLevelTitleID = this.xmlData.GetAttribute("HigherLevelTitleID");
                if (attrHigherLevelTitleID != null && attrHigherLevelTitleID.Value.Length > 0)
                {
                    higherLevelTitleID = attrHigherLevelTitleID.Value;
                    //this.mainBorder.MinWidth = 10;//普通标题限制最小宽度可以小一些。//会导致“添加同级标题”时标题宽度异常。
                }
                else
                {
                    //没有上级的ID说明是根标题。
                    //防止中心关系线画到框外来。
                    //this.mainBorder.MinWidth = 60;//会导致一个严重的问题：普通标题尺寸能大不能小。
                    this.baseDockPanel.HorizontalAlignment = HorizontalAlignment.Center;//防止最小宽度的限制造成左右不对称。
                }

                XmlAttribute attrPadding = this.xmlData.GetAttribute("Padding");
                if (attrPadding == null || string.IsNullOrEmpty(attrPadding.Value))
                {
                    this.mainBorder.Padding = normalTitlePadding;
                }
                else
                {
                    string[] values = attrPadding.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    if (values.Length == 4)
                    {
                        this.mainBorder.Padding = new Thickness(double.Parse(values[0]), double.Parse(values[1]),
                            double.Parse(values[2]), double.Parse(values[3]));
                    }
                    else
                    {
                        this.mainBorder.Padding = normalTitlePadding;
                    }
                }

                XmlAttribute attrForeground = XmlData.GetAttribute("TitleForeground");
                if (attrForeground != null)
                {
                    Brush brush = ColorPicker.GetBrushByName(attrForeground.Value);

                    mainTextBlock.Foreground = brush;
                    commentTextBlock.Foreground = brush;

                    OnPropertyChanged(this, new PropertyChangedEventArgs("TitleForeground"));
                }

                XmlAttribute attrBorderWidth = xmlData.GetAttribute("BorderWidth");
                if (attrBorderWidth != null)
                {
                    this.borderWidth = int.Parse(attrBorderWidth.Value);
                    mainBorder.BorderThickness = new Thickness(this.borderWidth);
                }

                XmlAttribute attrBorderColor = xmlData.GetAttribute("BorderColor");
                if (attrBorderColor != null)
                {
                    Brush bsh = ColorPicker.GetBrushByName(attrBorderColor.Value);
                    this.borderColor = bsh;
                    this.mainBorder.BorderBrush = bsh;
                }

                XmlAttribute attrRelationLineStroke = xmlData.GetAttribute("RelationLineStroke");
                if (attrRelationLineStroke != null)
                {
                    Brush bshOfRelationLineStroke = ColorPicker.GetBrushByName(attrRelationLineStroke.Value);
                    if (this.autoRelationPath != null)
                    {
                        this.autoRelationPath.Stroke = bshOfRelationLineStroke;
                    }

                    this.relationLineStroke = bshOfRelationLineStroke;
                }

                #region 已取消以Attribute方式存储备注文本这个方式。2011年11月20日
                //XmlAttribute attrComment = xmlData.GetAttribute("CommentText");
                //if (attrComment != null)
                //{
                //    this.commentText = attrComment.Value;
                //    commentTextBlock.Text = attrComment.Value;
                //    RefreshCommenttextVisible();

                //    OnPropertyChanged(this, new PropertyChangedEventArgs("CommentText"));
                //} 
                #endregion

                XmlAttribute attrRotateAngle = this.xmlData.GetAttribute("RotateAngle");
                if (attrRotateAngle != null)
                {
                    this.rotateAngle = double.Parse(attrRotateAngle.Value);
                    RefreshRotateAngle();
                }

                XmlAttribute attrCollapsedLowers = this.xmlData.GetAttribute("CollapsedLowers");
                if (attrCollapsedLowers != null)
                {
                    this.collapsedLowers = bool.Parse(attrCollapsedLowers.Value);
                }

                XmlAttribute attrIsOutBorderVisibled = this.xmlData.GetAttribute("IsOutBorderVisibled");
                if (attrIsOutBorderVisibled != null)
                {
                    this.isOutBorderVisibled = bool.Parse(attrIsOutBorderVisibled.Value);
                    RefreshOutBorderVisibled();
                }

                XmlAttribute attrQuestionText = this.xmlData.GetAttribute("QuestionText");
                if (attrQuestionText != null)
                {
                    this.QuestionText = attrQuestionText.Value;
                    this.RefreshQuestionText();
                }

                var iconXamlTextNode = this.xmlData.SelectSingleNode(XmlTags.IconXamlTextTag);
                if (iconXamlTextNode != null)
                {
                    this.iconXamlText = iconXamlTextNode.InnerXml;
                }

                RefreshCollapseAdorner();
                RefreshAddSameLevelTitleAdorner();
                RefreshAddLowerLevelTitleAdorner();
                RefreshTitleBackground();
                ReadText();

                RefreshMainTextBlockToolTip();
                ReadImages();

                this.RefreshIconXamlText();

                bool isFillBlankVisible = !(Globals.MainWindow.ckxForceFillBlankView.IsChecked == true);
                this.IsFillBlankVisible = isFillBlankVisible;

            }
            catch (Exception ex)
            {
                Dialogs.LunarMsgBox.MsgBoxShow(Globals.AppName,
                    "Title的BuildTitle()方法出错。异常信息如下：\r\n",
                    ex.Message, ex.StackTrace, MessageBoxButton.OK, MessageBoxImage.Error,
                    null, MessageBoxResult.OK, editor);
                return;
            }
        }

        private void ReadText()
        {
            Title.ReadTitleTextFromXmlData(this.XmlData, this.mainTextBlock, this);
            RefreshFillBlankVisible();
            Title.ReadCommentTextFromXmlData(this.XmlData, this.commentTextBlock, this);
        }

        public void RefreshMainTextBlockToolTip()
        {
            //if (this.higherLevelTitleID == null || this.higherLevelTitleID.Length <= 0 || this.IsFixed)
            //{
            //    //根标题
            //    this.mainTextBlock.ToolTip = null;
            //}
            //else
            //{
            //    this.mainTextBlock.ToolTip = new TextBlock()
            //    {
            //        Text = textBlockToolTip,
            //        TextAlignment = TextAlignment.Left,
            //    };
            //}
        }

        private void ReadImages()
        {
            imgStackPanel.Children.Clear();

            XmlNode imgSetNode = this.xmlData.SelectSingleNode("ImageSet");
            if (imgSetNode == null) return;

            XmlNodeList imgNodeList = imgSetNode.SelectNodes("Image");
            if (imgNodeList == null || imgNodeList.Count <= 0) return;

            foreach (XmlNode nImg in imgNodeList)
            {
                TitleImage ti = new TitleImage(editor, this);
                ti.XmlData = nImg;
                imgStackPanel.Children.Add(ti);
            }

            this.IsNeedSizeInvalid = true;
        }

        private Span autoNumberSpan = new Span();

        /// <summary>
        /// 用于自动编号的Span.
        /// </summary>
        public Span AutoNumberSpan
        {
            get { return autoNumberSpan; }
        }

        /// <summary>
        /// 刷新自动编号。
        /// </summary>
        public void RefreshAutoNumberString()
        {
            //根标题不进行自动编号。
            string s = this.AutoNumberString;
            if (this.higherLevelTitleID == null ||
                this.higherLevelTitleID.Length <= 0 ||
                s == null)
            {
                if (this.MainTextBlock.Inlines.Contains(this.autoNumberSpan))
                {
                    this.MainTextBlock.Inlines.Remove(this.autoNumberSpan);
                }
                return;
            }

            if (this.isFixed)//不需要自动编号。
            {
                if (this.MainTextBlock.Inlines.Contains(this.AutoNumberSpan))
                {
                    this.MainTextBlock.Inlines.Remove(this.AutoNumberSpan);
                }

                return;
            }

            //添加自动编号
            switch (Globals.MainWindow.View)
            {
                case ViewMode.HorizontalList:
                case ViewMode.VerticalList:
                    {
                        this.AutoNumberSpan.Inlines.Clear();
                        this.AutoNumberSpan.Inlines.Add(new Run(this.AutoNumberString));

                        if (this.MainTextBlock.Inlines.Contains(this.AutoNumberSpan) == false)
                        {
                            if (this.MainTextBlock.Inlines.FirstInline == null)
                            {
                                this.MainTextBlock.Inlines.Add(this.AutoNumberSpan);
                            }
                            else
                            {
                                this.MainTextBlock.Inlines.InsertBefore(
                                    this.MainTextBlock.Inlines.FirstInline,
                                    this.AutoNumberSpan);

                                this.mainTextBlock.InvalidateMeasure();
                                this.mainTextBlock.UpdateLayout();

                                this.IsNeedSizeInvalid = true;
                                this.RefreshNeedSize(beCollapsed);
                            }
                        }
                        RefreshAutoNumberColors();

                        break;
                    }
                default:
                    {
                        if (this.MainTextBlock.Inlines.Contains(this.AutoNumberSpan))
                        {
                            this.MainTextBlock.Inlines.Remove(this.AutoNumberSpan);
                        }
                        break;
                    }
            }
        }

        /// <summary>
        /// 使自动编号的前景、背景色与Title正文第一个字相同。
        /// </summary>
        private void RefreshAutoNumberColors()
        {
            Brush background = null;
            Brush foreground = null;
            double fontSize = -1;
            FontWeight fontWeight = FontWeights.Normal;
            FontStyle fontStyle = FontStyles.Normal;
            FontFamily fontFamily = null;

            if (this.MainTextBlock.Inlines.FirstInline != null)
            {
                var firstSpan = this.MainTextBlock.Inlines.FirstInline as Span;
                if (firstSpan == this.AutoNumberSpan) firstSpan = this.AutoNumberSpan.NextInline as Span;

                if (firstSpan != null)
                {
                    var firstInline = firstSpan.Inlines.FirstInline;
                    background = firstInline.Background;
                    foreground = firstInline.Foreground;
                    fontSize = firstInline.FontSize;
                    fontWeight = firstInline.FontWeight;
                    fontStyle = firstInline.FontStyle;
                    fontFamily = firstInline.FontFamily;
                }
                else return;
            }
            else return;

            foreach (Inline inline in this.AutoNumberSpan.Inlines)
            {
                inline.Background = background;
                inline.Foreground = (foreground == Brushes.Transparent) ? editor.DefaultFontColor : foreground;
                //将DefaultFontColor改为null会出错。

                if (fontSize > -1)
                {
                    inline.FontSize = fontSize;
                }
                else
                {
                    var higherTitle = this.GetHigherLvelTitle();
                    if (higherTitle != null && higherTitle.IsRootTitle)
                    {
                        inline.FontSize = (int)(Globals.MainWindow.DefaultFontSize * 6 / 5);
                    }
                    else
                    {
                        inline.FontSize = Globals.MainWindow.DefaultFontSize;
                    }
                }

                inline.FontWeight = fontWeight;
                inline.FontStyle = fontStyle;

                if (fontFamily != null) inline.FontFamily = fontFamily;
            }
        }

        public void RefreshDefaultFontSize()
        {
            this.ReadText();
        }

        public void ReadTitleTextFromXmlData()
        {
            ReadTitleTextFromXmlData(this.XmlData, this.mainTextBlock, this);
        }

        /// <summary>
        /// 根据XmlData中的TextSet中的文本内容来生成TextBlock的内容。
        /// </summary>
        /// <param name="xmlData">Xml数据。</param>
        /// <param name="textBlock">文本块。</param>
        /// <param name="title">文本块所在的富文本块（公式）。</param>
        public static void ReadTitleTextFromXmlData(XmlNode xmlData, TextBlock textBlock,
            Title title)
        {
            if (xmlData == null || textBlock == null) return;

            title.isUnderLineExist = false;
            textBlock.Text = "..标题..";
            textBlock.Inlines.Clear();

            if (title.IsRootTitle)
            {
                textBlock.FontSize = (int)(Globals.MainWindow.DefaultFontSize * 7 / 5);
            }
            else
            {
                var higherTitle = title.GetHigherLvelTitle();

                if (higherTitle != null && higherTitle.IsRootTitle)
                {
                    //SubRoot Title
                    textBlock.FontSize = (int)(Globals.MainWindow.DefaultFontSize * 6 / 5);
                }
                else
                {
                    //Normal Title
                    textBlock.FontSize = Globals.MainWindow.DefaultFontSize;
                }
            }

            XmlNode textSetNode = xmlData.SelectSingleNode("TextSet");
            if (textSetNode == null) return;

            XmlNodeList nodes = textSetNode.SelectNodes("Text");
            if (nodes == null || nodes.Count <= 0)
            {
                textBlock.Text = "";
                return;
            }

            title.RefreshAutoNumberString();

            Brush defaultTextBrush;

            if (title.TitleBackground == null || title.TitleBackground == Brushes.Transparent)
            {
                defaultTextBrush = Globals.MainWindow.IsLightDocumentBackground ? Brushes.Black : Brushes.White;
            }
            else
            {
                var bbsh = title.TitleBackground as SolidColorBrush;
                if (bbsh == null)
                {
                    defaultTextBrush = Globals.MainWindow.IsLightDocumentBackground ? Brushes.Black : Brushes.White;
                }
                else
                {
                    defaultTextBrush = Globals.IsLightColor(bbsh.Color) ? Brushes.Black : Brushes.White;
                }
            }

            if (nodes.Count == 1)
            {
                bool isUnderLine = false;
                var span = ReadTextNode(textBlock, title, nodes[0], ref isUnderLine, defaultTextBrush);

                //碰到下划线（作填空项）时，在前后各加个空格，以便更好地显示填空项。
                if (isUnderLine)
                {
                    //一个span中有且仅有一个run。
                    var run = span.Inlines.FirstInline as Run;
                    if (run.Text.StartsWith(" ") == false && run.Text.StartsWith("　") == false)
                    {
                        run.Text = " " + run.Text;
                    }

                    if (run.Text.EndsWith(" ") == false && run.Text.EndsWith("　") == false)
                    {
                        run.Text += "";
                    }

                    title.isUnderLineExist = true;
                }
            }
            else
            {
                List<CustomSpan> customSpans = new List<CustomSpan>();
                foreach (XmlNode node in nodes)
                {
                    bool isUnderLine = false;
                    if (node.InnerText == null || node.InnerText.Length == 0)
                    {
                        textBlock.Inlines.Add(new LineBreak());
                        continue;
                    }

                    var span = ReadTextNode(textBlock, title, node, ref isUnderLine, defaultTextBrush);
                    if (span != null)
                    {
                        var customSpan = new CustomSpan();
                        customSpan.Span = span; customSpan.IsUnderLine = isUnderLine;
                        customSpans.Add(customSpan);
                    }

                    //给带下划线的项（作填空项）首尾各加个半角空格，以突出显示。
                    //在这里处理是有道理的，因为很多时候带下划线的项会接连出现，这时候不能每个项首尾都加空格。

                    CustomSpan preSpan = null;
                    CustomSpan nextSpan = null;
                    Run preRun = null;
                    Run nextRun = null;
                    CustomSpan thisSpan = null;
                    Run thisRun = null;

                    for (int i = 0; i < customSpans.Count; i++)
                    {
                        if (i > 0)
                        {
                            preSpan = customSpans[i - 1];
                            preRun = preSpan.Span.Inlines.FirstInline as Run;
                        }

                        if (i < customSpans.Count - 1)
                        {
                            nextSpan = customSpans[i + 1];
                            nextRun = nextSpan.Span.Inlines.FirstInline as Run;
                        }

                        thisSpan = customSpans[i];
                        thisRun = thisSpan.Span.Inlines.FirstInline as Run;

                        if (thisSpan != null && thisRun != null && thisSpan.IsUnderLine)
                        {
                            if (preSpan == null || preRun == null || preSpan.IsUnderLine == false)
                            {
                                if (thisRun.Text.StartsWith(" ") == false && thisRun.Text.StartsWith("　") == false)
                                {
                                    thisRun.Text = " " + thisRun.Text;
                                }
                            }

                            if (nextSpan == null || nextRun == null || nextSpan.IsUnderLine == false)
                            {
                                if (thisRun.Text.EndsWith(" ") == false && thisRun.Text.EndsWith("　") == false)
                                {
                                    thisRun.Text += " ";
                                }
                            }

                            title.isUnderLineExist = true;
                        }

                        preSpan = null; preRun = null;
                        nextSpan = null; nextRun = null;
                        thisSpan = null; thisRun = null;
                    }
                }
            }

            title.RefreshTextBlockAlignment();
            title.RefreshAutoNumberColors();
        }

        /// <summary>
        /// ★如果要改造此方法，务必保证一个span中只有一个run。
        /// </summary>
        /// <param name="textBlock"></param>
        /// <param name="title"></param>
        /// <param name="node"></param>
        /// <returns></returns>
        private static Span ReadTextNode(TextBlock textBlock, Title title, XmlNode node, ref bool isUnderLine, Brush defaultTextBrush = null)
        {
            Span span = new Span();
            Run run = new Run(Dialogs.TitleTextEditor.RestoreXmlChars(node.InnerXml));
            span.Inlines.Add(run);

            //取文本特性
            XmlAttribute attrBackground = node.GetAttribute("Background");
            if (attrBackground != null)
            {
                Brush background = ColorPicker.GetBrushByName(attrBackground.Value);
                if (background != null)
                    run.Background = background;
            }
            else
            {
                run.Background = Brushes.Transparent;
            }

            span.Foreground = Brushes.Red;//这样做的目的是保证下划线、删除线均为红色。而字体由用户定义。

            XmlAttribute attrForeground = node.GetAttribute("Foreground");
            ReadForegroundOfRun(ref title, ref run, ref attrForeground);

            XmlAttribute attrFontSize = node.GetAttribute("FontSize");
            if (attrFontSize != null)
            {
                run.FontSize = double.Parse(attrFontSize.Value);
            }
            else
            {
                run.FontSize = textBlock.FontSize;
            }

            //改到主界面mainCanvas按缩放状态来改变文本渲染方式。
            //if (span.FontSize >= 15)//大于15号的字用柔和显示。
            //{
            //    TextOptions.SetTextFormattingMode(span, TextFormattingMode.Ideal);
            //}
            //else
            //{
            //    TextOptions.SetTextFormattingMode(span, TextFormattingMode.Display);
            //}

            XmlAttribute attrFontWeight = node.GetAttribute("FontWeight");
            if (attrFontWeight != null && attrFontWeight.Value == "Bold")
            {
                run.FontWeight = FontWeights.Bold;
            }

            XmlAttribute attrFontStyle = node.GetAttribute("FontStyle");
            if (attrFontStyle != null && attrFontStyle.Value == "Italic")
            {
                run.FontStyle = FontStyles.Italic;
            }

            XmlAttribute attrUnderLine = node.GetAttribute("UnderLine");
            if (attrUnderLine != null && attrUnderLine.Value == "True")
            {
                isUnderLine = true;

                span.TextDecorations.Add(Globals.UnderLineTextDecoration);
                //underLine因为要支持填空模式，所以不加诸run而是在span上设置。这样可以保证下划线总是可见。2013年10月6日

                span.Tag = "FillBlank";
            }
            else
            {
                span.Tag = null;
            }

            XmlAttribute attrStrikeLine = node.GetAttribute("StrikeLine");
            if (attrStrikeLine != null && attrStrikeLine.Value == "True")
            {
                span.TextDecorations.Add(TextDecorations.Strikethrough);
            }

            XmlAttribute attrFontName = node.GetAttribute("FontName");
            if (attrFontName != null)
            {
                FontFamily fontFamily = new FontFamily(attrFontName.Value);
                if (fontFamily != null)
                    run.FontFamily = fontFamily;
            }
            else
            {
                run.FontFamily = Globals.DefFontFamily;
            }

            XmlAttribute attrBaseLineAlignment = node.GetAttribute("BaseLineAlignment");
            if (attrBaseLineAlignment != null)
            {
                switch (attrBaseLineAlignment.Value)
                {
                    case "Top":
                        {
                            span.BaselineAlignment = BaselineAlignment.Top;

                            Binding fontSizeBinding = new Binding("ExFontSize");
                            fontSizeBinding.Source = title.Editor;
                            fontSizeBinding.Converter = MainWindow.halfTextSizeConverter;

                            span.SetBinding(Span.FontSizeProperty, fontSizeBinding);
                            break;
                        }
                    case "Bottom":
                        {
                            span.BaselineAlignment = BaselineAlignment.Bottom;

                            Binding fontSizeBinding = new Binding("ExFontSize");
                            fontSizeBinding.Source = title.Editor;
                            fontSizeBinding.Converter = MainWindow.halfTextSizeConverter;

                            span.SetBinding(Span.FontSizeProperty, fontSizeBinding);
                            break;
                        }
                    default:
                        {
                            span.BaselineAlignment = BaselineAlignment.Baseline;
                            break;
                        }
                }
            }

            textBlock.Inlines.Add(span);
            return span;
        }

        /// <summary>
        /// 给标题内 Comment 调用。
        /// </summary>
        private static void ReadForegroundOfSpan(ref Title title, ref Span span, ref XmlAttribute attrForeground)
        {
            if (title == null || span == null) return;

            if (attrForeground == null || string.IsNullOrWhiteSpace(attrForeground.Value)) return;

            // 如果用户特意指定了文本前景色，按用户指定的来
            Brush foreground = ColorPicker.GetBrushByName(attrForeground.Value);
            if (foreground != null)
            {
                span.Foreground = foreground;
                span.Tag = attrForeground.Value;  //Tag是填空模式要使用的。2013年10月6日
                return;
            }

            // 如果用户没有指定，则：
            // ① 看 Run 背景色

            if (span.Background != null && span.Background != Brushes.Transparent && span.Background is SolidColorBrush)
            {
                var rbgColor = (span.Background as SolidColorBrush).Color;
                if (Globals.IsLightColor(rbgColor))
                {
                    span.Foreground = Brushes.Black;
                }
                else
                {
                    span.Foreground = Brushes.White;
                }
                return;
            }

            // ②如果 Run 没有背景色，再看标题的背景色
            if (title.TitleBackground != null && title.TitleBackground != Brushes.Transparent && title.TitleBackground is SolidColorBrush)
            {
                var tbgColor = (title.TitleBackground as SolidColorBrush).Color;
                if (Globals.IsLightColor(tbgColor))
                {
                    span.Foreground = Brushes.Black;
                }
                else
                {
                    span.Foreground = Brushes.White;
                }
                return;
            }

            // ③如果标题也没有底色，再看文档背景色
            var docBack = Globals.MainWindow.mainCanvas.Background;
            var docsback = docBack as SolidColorBrush;
            // 文档底色只有几种纯色，再加个浅花纹（图像）画刷。
            if (docsback != null)
            {
                if (Globals.IsLightColor(docsback.Color))
                {
                    span.Foreground = Brushes.Black;
                }
                else
                {
                    span.Foreground = Brushes.White;
                }
            }
            else if (docBack is ImageBrush)  // 浅色
            {
                span.Foreground = Brushes.Black;
            }
        }

        /// <summary>
        /// 给标题文本调用。
        /// </summary>
        private static void ReadForegroundOfRun(ref Title title, ref Run run, ref XmlAttribute attrForeground)
        {
            if (title == null || run == null) return;

            if (attrForeground == null || string.IsNullOrWhiteSpace(attrForeground.Value)) return;

            // 如果用户特意指定了文本前景色，按用户指定的来
            Brush foreground = ColorPicker.GetBrushByName(attrForeground.Value);
            if (foreground != null)
            {
                run.Foreground = foreground;
                run.Tag = attrForeground.Value;  //Tag是填空模式要使用的。2013年10月6日
                return;
            }

            // 如果用户没有指定，则：
            // ① 看 Run 背景色

            if (run.Background != null && run.Background != Brushes.Transparent && run.Background is SolidColorBrush)
            {
                var rbgColor = (run.Background as SolidColorBrush).Color;
                if (Globals.IsLightColor(rbgColor))
                {
                    run.Foreground = Brushes.Black;
                }
                else
                {
                    run.Foreground = Brushes.White;
                }
                return;
            }

            // ②如果 Run 没有背景色，再看标题的背景色
            if (title.TitleBackground != null && title.TitleBackground != Brushes.Transparent && title.TitleBackground is SolidColorBrush)
            {
                var tbgColor = (title.TitleBackground as SolidColorBrush).Color;
                if (Globals.IsLightColor(tbgColor))
                {
                    run.Foreground = Brushes.Black;
                }
                else
                {
                    run.Foreground = Brushes.White;
                }
                return;
            }

            // ③如果标题也没有底色，再看文档背景色
            var docBack = Globals.MainWindow.mainCanvas.Background;
            var docsback = docBack as SolidColorBrush;
            // 文档底色只有几种纯色，再加个浅花纹（图像）画刷。
            if (docsback != null)
            {
                if (Globals.IsLightColor(docsback.Color))
                {
                    run.Foreground = Brushes.Black;
                }
                else
                {
                    run.Foreground = Brushes.White;
                }
            }
            else if (docBack is ImageBrush)  // 浅色
            {
                run.Foreground = Brushes.Black;
            }
        }

        /// <summary>
        /// 根据XmlData中的CommentSet中的文本内容来生成TextBlock的内容。
        /// </summary>
        /// <param name="xmlData">Xml数据。</param>
        /// <param name="commentTextBlock">文本块。</param>
        /// <param name="title">文本块所在的富文本块（公式）。</param>
        public static void ReadCommentTextFromXmlData(XmlNode xmlData, TextBlock commentTextBlock,
            Title title)
        {
            if (xmlData == null || commentTextBlock == null) return;

            commentTextBlock.Text = "..标题备注..";
            commentTextBlock.Inlines.Clear();

            XmlNode commentSetNode = xmlData.SelectSingleNode("CommentSet");

            #region 看看是不是没有必要显示备注文本。
            if (commentSetNode == null)
            {
                XmlAttribute attrComment = xmlData.GetAttribute("CommentText");
                if (attrComment != null)
                {
                    if (attrComment.Value.Length > 0)
                    {
                        commentSetNode = xmlData.AppendXml("<CommentSet><Text>" + attrComment.Value + "</Text></CommentSet>");
                    }

                    xmlData.RemoveAttributeByName("CommentText");

                    if (commentSetNode == null)
                    {
                        commentTextBlock.Text = "";//这将决定备注文本TextBlock是否显示。
                        title.RefreshCommentTextVisible();
                        return;
                    }
                    else
                    {
                        if (commentSetNode.FirstChild == null)
                        {
                            commentTextBlock.Text = "";//这将决定备注文本TextBlock是否显示。
                            title.RefreshCommentTextVisible();
                            return;
                        }

                        if (commentSetNode.ChildNodes.Count <= 0)
                        {
                            commentTextBlock.Text = "";//这将决定备注文本TextBlock是否显示。
                            title.RefreshCommentTextVisible();
                            return;
                        }
                    }
                }
                else
                {
                    commentTextBlock.Text = "";//这将决定备注文本TextBlock是否显示。
                    title.RefreshCommentTextVisible();
                    return;
                }
            }
            #endregion

            XmlNodeList nodes = commentSetNode.SelectNodes("Text");
            if (nodes == null || nodes.Count <= 0)
            {
                commentTextBlock.Text = "";
                title.RefreshCommentTextVisible();
                return;
            }

            Brush defaultTextBrush;

            if (title.TitleBackground == null || title.TitleBackground == Brushes.Transparent)
            {
                defaultTextBrush = Globals.MainWindow.IsLightDocumentBackground ? Brushes.Black : Brushes.White;
            }
            else
            {
                var bbsh = title.TitleBackground as SolidColorBrush;
                if (bbsh == null)
                {
                    defaultTextBrush = Globals.MainWindow.IsLightDocumentBackground ? Brushes.Black : Brushes.White;
                }
                else
                {
                    defaultTextBrush = Globals.IsLightColor(bbsh.Color) ? Brushes.Black : Brushes.White;
                }
            }

            foreach (XmlNode node in nodes)
            {
                if (node.InnerText == null || node.InnerText.Length == 0)
                {
                    commentTextBlock.Inlines.Add(new LineBreak());
                    continue;
                }

                Span span = new Span();
                span.Inlines.Add(new Run(Dialogs.TitleTextEditor.RestoreXmlChars(node.InnerXml)));

                //取文本特性
                XmlAttribute attrBackground = node.GetAttribute("Background");
                if (attrBackground != null)
                {
                    Brush background = ColorPicker.GetBrushByName(attrBackground.Value);
                    if (background != null)
                        span.Background = background;
                }
                else
                {
                    span.Background = Brushes.Transparent;
                }

                XmlAttribute attrForeground = node.GetAttribute("Foreground");
                ReadForegroundOfSpan(ref title, ref span, ref attrForeground);

                XmlAttribute attrFontSize = node.GetAttribute("FontSize");
                if (attrFontSize != null)
                {
                    span.FontSize = double.Parse(attrFontSize.Value);
                }
                else
                {
                    span.FontSize = commentTextBlock.FontSize;
                }

                //改到主界面mainCanvas按缩放状态来改变文本渲染方式。
                //if (span.FontSize < 15)
                //{
                //    TextOptions.SetTextFormattingMode(span, TextFormattingMode.Display);
                //}
                //else
                //{
                //    TextOptions.SetTextFormattingMode(span, TextFormattingMode.Ideal);
                //}

                XmlAttribute attrFontWeight = node.GetAttribute("FontWeight");
                if (attrFontWeight != null && attrFontWeight.Value == "Bold")
                {
                    span.FontWeight = FontWeights.Bold;
                }

                XmlAttribute attrFontStyle = node.GetAttribute("FontStyle");
                if (attrFontStyle != null && attrFontStyle.Value == "Italic")
                {
                    span.FontStyle = FontStyles.Italic;
                }

                XmlAttribute attrUnderLine = node.GetAttribute("UnderLine");
                if (attrUnderLine != null && attrUnderLine.Value == "True")
                {
                    //span.TextDecorations.Add(TextDecorations.Underline);
                    span.TextDecorations.Add(Globals.UnderLineTextDecoration);
                }

                XmlAttribute attrStrikeLine = node.GetAttribute("StrikeLine");
                if (attrStrikeLine != null && attrStrikeLine.Value == "True")
                {
                    span.TextDecorations.Add(TextDecorations.Strikethrough);
                }

                XmlAttribute attrFontName = node.GetAttribute("FontName");
                if (attrFontName != null)
                {
                    FontFamily fontFamily = new FontFamily(attrFontName.Value);
                    if (fontFamily != null)
                        span.FontFamily = fontFamily;
                }

                XmlAttribute attrBaseLineAlignment = node.GetAttribute("BaseLineAlignment");
                if (attrBaseLineAlignment != null)
                {
                    switch (attrBaseLineAlignment.Value)
                    {
                        case "Top":
                            {
                                span.BaselineAlignment = BaselineAlignment.Top;

                                Binding fontSizeBinding = new Binding("ExFontSize");
                                fontSizeBinding.Source = title.Editor;
                                fontSizeBinding.Converter = MainWindow.halfTextSizeConverter;

                                span.SetBinding(Span.FontSizeProperty, fontSizeBinding);
                                break;
                            }
                        case "Bottom":
                            {
                                span.BaselineAlignment = BaselineAlignment.Bottom;

                                Binding fontSizeBinding = new Binding("ExFontSize");
                                fontSizeBinding.Source = title.Editor;
                                fontSizeBinding.Converter = MainWindow.halfTextSizeConverter;

                                span.SetBinding(Span.FontSizeProperty, fontSizeBinding);
                                break;
                            }
                        default:
                            {
                                span.BaselineAlignment = BaselineAlignment.Baseline;
                                break;
                            }
                    }
                }

                commentTextBlock.Inlines.Add(span);
            }

            title.RefreshCommentTextVisible();
        }

        public void RefreshTextBlockAlignment()
        {
            if (this.higherLevelTitleID == null || this.higherLevelTitleID.Length <= 0)
            {
                //RootTile，有备注时左齐，没有备注时居中对齐。
                XmlNode commentSetNode = this.XmlData.SelectSingleNode("CommentSet");

                if (commentSetNode != null && commentSetNode.ChildNodes.Count > 0)
                {
                    this.MainTextBlock.TextAlignment = TextAlignment.Left;
                }
                else
                {
                    this.MainTextBlock.TextAlignment = TextAlignment.Center;
                }
                return;
            }
            else
            {
                this.mainTextBlock.TextAlignment = TextAlignment.Left;
                this.commentTextBlock.TextAlignment = TextAlignment.Left;
            }

            //if (this.Direction == TitleDirection.Right ||
            //    (this.editor != null && this.editor.View != ViewMode.Default))//横向、纵向列表视图中，总是左对齐文本的。
            //{
            //    this.MainTextBlock.TextAlignment = TextAlignment.Left;
            //    this.CommentTextBlock.TextAlignment = TextAlignment.Left;
            //}
            //else
            //{
            //    //多行时，右对齐文本有些反人类。
            //    this.MainTextBlock.TextAlignment = TextAlignment.Right;
            //    this.CommentTextBlock.TextAlignment = TextAlignment.Right;
            //}

            this.IsNeedSizeInvalid = true;
        }

        private bool isSelected = false;

        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                isSelected = value;

                RefreshTitleSelectedAdorner();

                RefreshAddLowerLevelTitleAdorner();
                RefreshAddSameLevelTitleAdorner();
                RefreshCollapseAdorner();
            }
        }

        /// <summary>
        /// 尝试滚动编辑区以显示标题。此方法不同于MainWindow.TryScrollTitle()方法。
        /// 此方法用于编辑时尝试显示正在编辑的标题，避免被编辑窗口（在主窗口底侧位置）遮挡。而MainWindow.TryScrollTitle()用于演示。
        /// 当标题尺寸过大时，这个功能不起作用。
        /// </summary>
        /// <param name="title"></param>
        internal static void TryToShowTitleInVisualArea(Title title, bool moreBottomBlank = false)
        {
            if (title == null || title.editor == null) return;

            if (title.editor.sliderOfCanvasScale.Value != 0) return;

            if (title.ActualHeight > title.editor.mainScrollViewer.ActualHeight ||
                title.ActualWidth > title.editor.mainScrollViewer.ActualWidth)
                return;

            double vOffcet = title.editor.mainScrollViewer.VerticalOffset;
            double hOffcet = title.editor.mainScrollViewer.HorizontalOffset;

            Point ptTopLeft = new Point(0, 0);
            Point ptTopRight = new Point(title.ActualWidth, 0);
            Point ptBottomLeft = new Point(0, title.ActualHeight);
            Point ptBottomRight = new Point(title.ActualWidth, title.ActualHeight);

            Point rTopLeft = title.TranslatePoint(ptTopLeft, title.editor.mainScrollViewer);
            if (rTopLeft.X < 0)
            {
                title.editor.mainScrollViewer.ScrollToHorizontalOffset(hOffcet + rTopLeft.X);
            }
            else
            {
                if (rTopLeft.X + title.ActualWidth > title.editor.mainScrollViewer.ActualWidth - 20)
                {
                    title.editor.mainScrollViewer.ScrollToHorizontalOffset(hOffcet +
                        rTopLeft.X + title.ActualWidth - (title.editor.mainScrollViewer.ActualWidth - 20));
                }

            }

            if (rTopLeft.Y < 0)
            {
                title.editor.mainScrollViewer.ScrollToVerticalOffset(vOffcet + rTopLeft.Y);
            }
            else
            {
                if (moreBottomBlank)
                {

                    if (rTopLeft.Y + title.ActualHeight > title.editor.mainScrollViewer.ActualHeight - 140)
                    {
                        title.editor.mainScrollViewer.ScrollToVerticalOffset(vOffcet +
                            rTopLeft.Y + title.ActualHeight - (title.editor.mainScrollViewer.ActualHeight - 140));
                    }
                }
                else
                {
                    if (rTopLeft.Y + title.ActualHeight > title.editor.mainScrollViewer.ActualHeight - 20)
                    {
                        title.editor.mainScrollViewer.ScrollToVerticalOffset(vOffcet +
                            rTopLeft.Y + title.ActualHeight - (title.editor.mainScrollViewer.ActualHeight - 20));
                    }
                }
            }
        }

        protected int index = 0;

        /// <summary>
        /// 在同级标题之中的Index。根标题应保持此值为０.
        /// </summary>
        public int Index
        {
            get { return index; }
            set
            {
                index = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("Index", value.ToString());
                }

                OnPropertyChanged(this, new PropertyChangedEventArgs("Index"));
            }
        }

        /// <summary>
        /// 这是个只读属性，指的是此标题距离RootTitle的距离。
        /// 同级之间的索引应使用Index属性。
        /// </summary>
        public int LevelIndex
        {
            get
            {
                int v = 0;

                Title higherTitle = this.GetHigherLvelTitle();
                while (higherTitle != null)
                {
                    v++;
                    higherTitle = higherTitle.GetHigherLvelTitle();
                }

                return v;
            }
        }

        /// <summary>
        /// 如果同级只有一个标题，返回null。
        /// </summary>
        public string AutoNumberString
        {
            get
            {
                int levelIndex = LevelIndex;

                if (index == 0)
                {
                    Title higherTitle = this.GetHigherLvelTitle();
                    if (higherTitle == null || higherTitle.LowerLevelTitles.Count <= 1)
                    {
                        return null;
                    }
                }

                int number = Index + 1;
                switch (levelIndex)
                {
                    case 1:
                        {
                            return Globals.GetChineseNumber(number) + "、";
                        }
                    case 2:
                        {
                            return "（" + Globals.GetChineseNumber(number) + "）";
                        }
                    case 3:
                        {
                            return number.ToString() + "．";
                        }
                    case 4:
                        {
                            return "（" + number.ToString() + "）";
                        }
                }

                return "";
            }
        }

        private bool isMainSelectedTitle = false;

        /// <summary>
        /// 如果IsSelected不为true，则应保证此属性不起任何作用。
        /// </summary>
        public bool IsMainSelectedTitle
        {
            get
            {
                //如果isSelected为假，则此值总是为假。
                if (isSelected == false)
                {
                    isMainSelectedTitle = false;
                    return false;
                }

                return isMainSelectedTitle;
            }
            set
            {
                if (isSelected)
                {
                    isMainSelectedTitle = value;

                    if (isMainSelectedTitle)
                    {
                        RefreshEditorTitleDraggingHandler();
                    }

                    //刷新主界面上的滑块条的值。
                    if (editor != null)
                    {
                        if (editor.SelectedTitlesList.Contains(this) &&
                            this.IsMainSelectedTitle)
                        {
                            editor.sliderBorderWidth.Value = this.borderWidth;
                        }
                    }

                    if (isMainSelectedTitle && editor != null)
                    {
                        editor.rotateSlider.Value = rotateAngle;

                        //刷新计时器。
                        if (editor.ckxAutoPresentation.IsChecked == true)
                        {
                            editor.SetTimerInterval();
                        }
                    }
                }
                else
                {
                    isMainSelectedTitle = false;//如果isSelected为假，则此值总是为假。

                    //刷新主界面上的滑块条的值。
                    if (editor != null)
                    {
                        editor.sliderBorderWidth.Value = 1;
                    }
                }

                RefreshTitleSelectedAdorner();

                RefreshAddLowerLevelTitleAdorner();
                RefreshAddSameLevelTitleAdorner();
                RefreshCollapseAdorner();

                if (isMainSelectedTitle)
                {
                    RefreshTbxCaret();

                    if (this.editor != null)
                    {
                        this.editor.RefreshMainSelectedTitle(this);//显示“标题被选中图形”（原来是齿轮，后来改成了其它图形）
                        this.editor.RefreshMousePoint();
                    }
                }
                else
                {
                    if (this.editor != null)
                    {
                        this.editor.RefreshMainSelectedTitle(null);//隐藏“标题被选中图形”（原来是齿轮，后来改成了其它图形）
                        this.editor.RefreshMousePoint();
                    }
                }

                if (editor != null)
                {
                    if (this.IsFixed)
                    {
                        editor.tbkTitleGlobalIndex.Text = "";
                    }
                    else editor.tbkTitleGlobalIndex.Text = (this.GlobalIndex + 1).ToString();
                }
            }
        }

        internal void RefreshEditorTitleDraggingHandler()
        {
            if (editor == null) return;

            editor.isDraggingTitle = false;//防止在拖动的过程中用方向键另选标题。

            Title rootTitle = editor.RootTitle;

            if (rootTitle == this || this.isFixed)//根标题永远不可能被拖动。一级标题可能被拖动到其它一级标题下侧。
            {
                editor.titleDraggingHandler.Visibility = Visibility.Hidden;
                return;
            }

            if (editor.mainCanvas.Children.Contains(editor.titleDraggingHandler) == false)
            {
                editor.mainCanvas.Children.Add(editor.titleDraggingHandler);
                Canvas.SetZIndex(editor.titleDraggingHandler, 99);//Title是100
                editor.titleDraggingHandler.Visibility = Visibility.Visible;
            }
            else
            {
                if (isSelected && isMainSelectedTitle)
                {
                    editor.titleDraggingHandler.Visibility = Visibility.Visible;
                }
            }

            switch (Globals.MainWindow.View)
            {
                case ViewMode.HorizontalList:
                case ViewMode.VerticalList://这两种视图模式除根标题与一级标题的布局不同外都一样。也都不考虑标题的Indent。
                    {
                        Point leftCenter = new Point(this.leftTop.X - editor.titleDraggingHandler.Width + 4,
                                this.leftTop.Y + this.ActualHeight / 2);

                        Canvas.SetLeft(editor.titleDraggingHandler, leftCenter.X);
                        Canvas.SetTop(editor.titleDraggingHandler, leftCenter.Y - editor.titleDraggingHandler.Height / 2);
                        break;
                    }
                default:
                    {
                        if (direction == TitleDirection.Right)
                        {
                            Point leftCenter = new Point(this.leftTop.X - editor.titleDraggingHandler.Width + IndentWidth + 4,
                                this.leftTop.Y + this.ActualHeight / 2);

                            Canvas.SetLeft(editor.titleDraggingHandler, leftCenter.X);
                            Canvas.SetTop(editor.titleDraggingHandler, leftCenter.Y - editor.titleDraggingHandler.Height / 2);
                        }
                        else
                        {
                            Point leftCenter = new Point(this.leftTop.X + this.ActualWidth - IndentWidth - 4, this.leftTop.Y + this.ActualHeight / 2);

                            Canvas.SetLeft(editor.titleDraggingHandler, leftCenter.X);
                            Canvas.SetTop(editor.titleDraggingHandler, leftCenter.Y - editor.titleDraggingHandler.Height / 2);
                        }
                        break;
                    }
            }

            if (!isFixed)
            {
                editor.titleDraggingHandler.ToolTip = titleHandlerToolTip;
            }
            else
            {
                editor.titleDraggingHandler.ToolTip = fixedTitleHandlerToolTip;
            }
        }

        private static string titleHandlerToolTip = "可拖动到另一标题内部";

        private static string fixedTitleHandlerToolTip = "拖动以移动位置";

        private static Thickness normalTitlePadding = new Thickness(5, 3, 5, 3);

        private double rotateAngle = 0;

        public double RotateAngle
        {
            get { return rotateAngle; }
            set
            {
                if (value > 180)
                {
                    rotateAngle = 180;
                }
                else if (value < -180)
                {
                    rotateAngle = -180;
                }
                else
                {
                    rotateAngle = value;
                }

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("RotateAngle", rotateAngle.ToString());
                }

                RefreshRotateAngle();
            }
        }

        private bool isOutBorderVisibled = false;

        public bool IsOutBorderVisibled
        {
            get { return isOutBorderVisibled; }
            set
            {
                isOutBorderVisibled = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("IsOutBorderVisibled", value.ToString());
                }

                RefreshOutBorderVisibled();
            }
        }

        private TitleOutBorderAdorner outBorderAdorner = null;

        internal void RefreshOutBorderVisibled()
        {
            if (isOutBorderVisibled == false)
            {
                if (outBorderAdorner == null) return;
                else
                {
                    outBorderAdorner.Visibility = Visibility.Collapsed;
                    return;
                }
            }

            if (outBorderAdorner == null)
            {
                outBorderAdorner = new TitleOutBorderAdorner(this.mainBorder, this);
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.mainBorder);
                adornerLayer.Add(outBorderAdorner);
            }

            outBorderAdorner.Visibility = Visibility.Visible;
            outBorderAdorner.InvalidateVisual();
        }

        private static readonly Point defRenderCenter = new Point(0.5, 0.5);
        private static readonly RotateTransform defRotateTransform = new RotateTransform(0);

        private void RefreshRotateAngle()
        {
            if (this.RenderTransformOrigin != defRenderCenter)
            {
                this.RenderTransformOrigin = defRenderCenter;
            }

            if (rotateAngle == 0)
            {
                this.RenderTransform = defRotateTransform;
            }
            else
            {
                this.RenderTransform = new RotateTransform(rotateAngle);
            }
        }

        /// <summary>
        /// 刚刚才添加的新Title暂时还没有leftTop坐标值，此时不起作用。需要在插入并布局后手工调用此方法。
        /// </summary>
        public void RefreshTbxCaret()
        {
            if (editor == null) return;

            if (editor.mainCanvas.Children.Contains(editor.tbxCaret) == false)
            {
                editor.mainCanvas.Children.Add(editor.tbxCaret);
                Canvas.SetZIndex(editor.tbxCaret, 0);
            }

            editor.tbxCaret.Width = this.mainTextBlock.Width;
            editor.tbxCaret.Height = this.mainTextBlock.Height;

            Canvas.SetLeft(editor.tbxCaret, Canvas.GetLeft(this) +
                this.mainBorder.BorderThickness.Left + this.mainBorder.Margin.Left);
            Canvas.SetTop(editor.tbxCaret, Canvas.GetTop(this) +
                this.mainBorder.BorderThickness.Top + this.mainBorder.Margin.Top + 2);

            editor.tbxCaret.InvalidateArrange();
            editor.tbxCaret.UpdateLayout();

            editor.tbxCaret.Focus();
            editor.tbxCaret.Select(0, 0);
        }

        public void RefreshTitleSelectedAdorner()
        {
            //if (isSelected)
            //{
            //    selectedAdorner.Visibility = Visibility.Visible;
            //}
            //else
            //{
            //    selectedAdorner.Visibility = Visibility.Hidden;
            //}
            //2013年8月16日，现在，没有选定也要显示，但显示的不同。

            this.selectedAdorner.InvalidateVisual();
        }

        private TitleSelectedAdorner selectedAdorner;

        private TitleQuestionTooltipAdorner questionTooltipAdorner;

        /// <summary>
        /// 用以表示当前标题被选中的装饰器。
        /// </summary>
        public TitleSelectedAdorner SelectedAdorner
        {
            get { return selectedAdorner; }
        }

        private double blankAreaWidth = Globals.BlankAreaWidth;

        /// <summary>
        /// 标题与下级标题之间的宽度。根标题与一极标题[SubRoot]之间的宽度不受影响。
        /// </summary>
        public double BlankAreaWidth
        {
            get { return blankAreaWidth; }
        }

        /// <summary>
        /// 如果用作概要的备注文本块过高，此时排列第一个下级标题时就应该考虑这个差值。
        /// </summary>
        private double baseTopAddValueFromSummary = 0;

        public double BaseTopAddValueFromSummary
        {
            get { return baseTopAddValueFromSummary; }
        }

        private double baseTopAddValueWhenHeightBigThanLowersHeights = 0;

        public double BaseTopAddValueWhenHeightBigThanLowersHeights
        {
            get { return baseTopAddValueWhenHeightBigThanLowersHeights; }
        }

        private bool isNeedSizeInvalid = false;
        /// <summary>
        /// 此标题尺寸变化时，或者此标题的下级标题尺寸变化时，或者下级标题尺寸变化时，此值真。
        /// </summary>
        public bool IsNeedSizeInvalid
        {
            get { return this.isNeedSizeInvalid; }
            set
            {
                this.isNeedSizeInvalid = value;

                if (this.isNeedSizeInvalid == true)
                {
                    if (this.editor != null)
                    {
                        var higher = this.editor.GetTitleByID(this.HigherLevelTitleID);
                        while (higher != null)
                        {
                            higher.isNeedSizeInvalid = true;//不能用属性，否则会重复很多次。
                            higher = this.editor.GetTitleByID(higher.HigherLevelTitleID);
                        }
                    }
                }//如果尺寸未变，无须通知上级。
            }
        }

        /// <summary>
        /// 
        /// 根据视图模式不同，根标题这个值也不同。因此根标题不应调用此方法获取值。
        /// 根标题应在“MainWindow.xaml.cs”文件中自实行实现方法来计算这个值。
        /// 
        /// 此方法会先调用GetLowerLevelTitles()方法。
        /// 因此，如果要使用LowerLevelTitles，应在此方法被调用后立即使用，否则不能保证准确性。
        /// </summary>
        public void RefreshNeedSize(bool collapseFromSomeOneHigher)
        {
            if (this.IsNeedSizeInvalid == false) return;//不需要刷新。

            this.blankAreaWidth = Globals.BlankAreaWidth;//需要复位此值，否则会在“折叠”状态下误取“附加宽度”的值，导致与上级标题间距过大。

            this.beCollapsed = collapseFromSomeOneHigher;

            if (this.beCollapsed)
            {
                this.Visibility = Visibility.Collapsed;
                if (IsSelected) { IsSelected = false; }
                if (IsMainSelectedTitle) { IsMainSelectedTitle = false; }
            }
            else
            {
                this.Visibility = Visibility.Visible;
                if (IsFixed)
                {
                    needSize.Width = this.ActualWidth;
                    needSize.Height = this.ActualHeight;
                    return;
                }
            }

            bool newCollapseParm = false;
            if (this.beCollapsed || this.collapsedLowers)
            {
                newCollapseParm = true;
            }

            Comment summary = SummaryComment;

            List<Title> lowTitles = GetLowerLevelTitles();
            if (lowTitles != null && lowTitles.Count > 0)
            {
                double lowerHeightsSum = 0;//高只需要比较“自身的高度”与“所有下级共有高度之和”及概要的高度，取其中较大的值即可。
                double maxWidthOfLowers = 0;

                foreach (Title t in lowTitles)
                {
                    t.RefreshNeedSize(newCollapseParm);

                    lowerHeightsSum += t.NeedSize.Height;

                    if (maxWidthOfLowers < t.NeedSize.Width)
                    {
                        maxWidthOfLowers = t.NeedSize.Width;
                    }
                }

                if (collapseFromSomeOneHigher)
                {
                    needSize.Width = needSize.Height = 0;
                }
                else//如果没有被某个上级给折叠掉。
                {
                    if (this.collapsedLowers)
                    {
                        needSize.Width = this.ActualWidth + this.IndentWidth;
                        needSize.Height = this.ActualHeight;
                    }
                    else//如果自身也不折叠下级。
                    {
                        switch (Globals.MainWindow.View)
                        {
                            case ViewMode.HorizontalList:
                            case ViewMode.VerticalList://根标题不应使用此方法。其余标题这两种视图模式下计算结果相同。
                                {
                                    baseTopAddValueWhenHeightBigThanLowersHeights = 0;

                                    needSize.Width = Math.Max(this.ActualWidth, Globals.BlankAreaWidth + maxWidthOfLowers);
                                    needSize.Height = this.ActualHeight + lowerHeightsSum;

                                    if (summary != null)
                                    {
                                        double summaryHeight = summary.ActualHeight + 30;//30是备注文本块的上下边框的总高度。

                                        if (summaryHeight > lowerHeightsSum + this.ActualHeight)
                                        {
                                            baseTopAddValueFromSummary = (summaryHeight - (lowerHeightsSum + this.ActualHeight)) / 2;
                                        }
                                        else
                                        {
                                            baseTopAddValueFromSummary = 0;
                                        }

                                        needSize.Height = Math.Max(needSize.Height, summaryHeight);
                                    }
                                    break;
                                }
                            default:
                                {
                                    if (this.ActualHeight > lowerHeightsSum)
                                    {
                                        needSize.Height = this.ActualHeight;
                                        baseTopAddValueWhenHeightBigThanLowersHeights = (this.ActualHeight - lowerHeightsSum) / 2;
                                    }
                                    else
                                    {
                                        baseTopAddValueWhenHeightBigThanLowersHeights = 0;
                                        needSize.Height = lowerHeightsSum;

                                        if (summary != null)
                                        {
                                            double summaryHeight = summary.ActualHeight + 30;//30是备注文本块的上下边框的总高度。

                                            if (summaryHeight > lowerHeightsSum)
                                            {
                                                baseTopAddValueFromSummary = (summaryHeight - lowerHeightsSum) / 2;
                                            }
                                            else
                                            {
                                                baseTopAddValueFromSummary = 0;
                                            }
                                        }
                                    }

                                    if (this.CollapsedLowers != true)
                                    {
                                        double additionWidth = lowerHeightsSum * 0.1;//附加宽度，如果折叠了下级，不需要附加宽度。

                                        blankAreaWidth = Math.Max(additionWidth, blankAreaWidth);
                                    }
                                    else
                                    {
                                        blankAreaWidth = Globals.BlankAreaWidth;
                                    }

                                    needSize.Width = this.ActualWidth + this.IndentWidth + blankAreaWidth + maxWidthOfLowers;
                                    break;
                                }
                        }
                    }
                }
            }
            else//如果没有下级。
            {
                if (beCollapsed)
                {
                    needSize.Width = needSize.Height = 0;
                }
                else
                {
                    needSize.Height = ActualHeight;
                    needSize.Width = ActualWidth + this.IndentWidth;
                }
            }

            if (summary != null)
            {
                needSize.Width += (summary.ActualWidth + 60);//30是备注文本块的左右边框的总宽度，另30则是给括弧留下的宽度。
                needSize.Height = Math.Max(needSize.Height, summary.ActualHeight + 30);//30是备注文本块的上下边框的总高度。
            }

            //一级标题还是更大些。

            if (this.Editor != null && this.Editor.View == ViewMode.Default)
            {
                if (this.GetHigherLvelTitle() == this.Editor.RootTitle)
                {
                    needSize.Height = Math.Max(needSize.Height, 80);//一级标题最小高度80，这样在没有下级时会匀称点。
                }
            }


            this.isNeedSizeInvalid = false;//不能用属性。
        }

        #region 这两个方法只会调用一个。

        /// <summary>
        /// 包含自身与下级及间接下级的所有区域的左上角。
        /// 在LocateAtLeft()或LocateAtRight()方法被调用时，会更新这个值。
        /// </summary>
        private Point basePointOfControlRect = new Point();

        /// <summary>
        /// 表示控制区基准点。
        /// 如果是位于根标题右侧，则表示控制区左上角坐标。
        /// 如果是位于根标题左侧的，则表示控制区右上角坐标。——正好相反。
        /// </summary>
        public Point BasePointOfControlRect
        {
            get { return basePointOfControlRect; }
        }

        protected Point leftTop = new Point();

        /// <summary>
        /// 在LocateAtLeft()或LocateAtRight()方法被调用时，会更新这个值。
        /// </summary>
        internal Point LeftTop { get { return leftTop; } }

        /// <summary>
        /// 只有根标题才应调用此方法。
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        internal virtual void SetLeftTop(double x, double y)
        {
            if (this.higherLevelTitleID == null || higherLevelTitleID.Length <= 0)
            {
                leftTop.X = x;
                leftTop.Y = y;
            }
            else
            {
                MessageBox.Show("只有根标题才应调用Title.SetLeftTop()方法。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
        }

        /// <summary>
        /// 
        /// ①根标题和一级标题[SubRoot]不应调用此方法。
        /// ②由于只有根标题和一级标题[SubRoot]不同，因此无论是HorizontalList还是VerticalList都调用此方法。
        /// 
        /// 不问direction，全部右向（更符合中文阅读习惯）；不问Indent级别，因为本就是HorizontalList。
        /// </summary>
        /// <param name="basePt"></param>
        internal void LocateAsListViewItem(Point basePt)
        {
            basePointOfControlRect.X = basePt.X;
            basePointOfControlRect.Y = basePt.Y;

            double indentWidth = this.IndentWidth;

            //先定自身位置
            leftTop.X = basePt.X;
            leftTop.Y = basePt.Y + baseTopAddValueFromSummary;

            if (Canvas.GetLeft(this) != leftTop.X)
            {
                Canvas.SetLeft(this, leftTop.X);
            }

            if (Canvas.GetTop(this) != leftTop.Y)
            {
                Canvas.SetTop(this, leftTop.Y);
            }

            //这里不能直接使用blankAreaWidth字段——因为其值可能不符合HorizontalListView的需要。
            double baseLeft = basePt.X + Globals.BlankAreaWidth;
            double baseTop = basePt.Y + baseTopAddValueFromSummary + this.ActualHeight;

            for (int i = 0; i < lowerLevelTitles.Count; i++)
            {
                Title t = lowerLevelTitles[i];

                t.LocateAsListViewItem(new Point(baseLeft, baseTop));

                baseTop += t.NeedSize.Height;
            }

            RefreshOutBorderVisibled();
            RefreshAutoNumberString();//只有列表视图才需要
        }

        /// <summary>
        /// 根标题不应调用此方法。调用此方法前必须调用GetLowerLevelTitles()方法，
        /// 以保证LowerLevelTitles中的值有效。
        /// </summary>
        /// <param name="basePt">右上角的起始坐标。</param>
        internal void LocateAtLeft(Point basePt)
        {
            basePointOfControlRect.X = basePt.X;
            basePointOfControlRect.Y = basePt.Y;

            double indentWidth = this.IndentWidth;

            //先定自身位置
            leftTop.X = basePt.X - this.ActualWidth;

            Title higherTitle = this.GetHigherLvelTitle();
            if (higherTitle != null)
            {
                List<Title> brothers = higherTitle.GetLowerLevelTitles();
                if (brothers.Count == 1 && brothers[0] == this && higherTitle.ActualHeight == this.ActualHeight)
                {
                    leftTop.Y = higherTitle.LeftTop.Y;
                }
                else
                {
                    leftTop.Y = basePt.Y + (this.needSize.Height - this.ActualHeight) / 2;
                }
            }
            else
            {
                leftTop.Y = basePt.Y + (this.needSize.Height - this.ActualHeight) / 2;
            }

            double newLeft = leftTop.X - indentWidth;
            if (Canvas.GetLeft(this) != newLeft)
            {
                Canvas.SetLeft(this, newLeft);
            }

            double newTop = leftTop.Y;
            if (Canvas.GetTop(this) != newTop)
            {
                Canvas.SetTop(this, leftTop.Y);
            }

            ResetDirection(TitleDirection.Left);

            double baseLeft = basePt.X - indentWidth - this.ActualWidth - blankAreaWidth;
            double baseTop = basePt.Y + baseTopAddValueFromSummary + baseTopAddValueWhenHeightBigThanLowersHeights;

            for (int i = 0; i < lowerLevelTitles.Count; i++)
            {
                Title t = lowerLevelTitles[i];

                t.LocateAtLeft(new Point(baseLeft, baseTop));
                if (t.Direction != TitleDirection.Left)
                {
                    t.direction = TitleDirection.Left;
                }
                baseTop += t.NeedSize.Height;
            }

            RefreshOutBorderVisibled();
            RefreshAutoNumberString();//默认视图本身不需要自动编号，但是需要取消自动编号

            if (IsMainSelectedTitle && this.editor != null)
            {
                this.editor.RefreshMainSelectedTitle(this);
            }
        }

        /// <summary>
        /// 根标题不应调用此方法。调用此方法前必须调用GetLowerLevelTitles()方法，
        /// 以保证LowerLevelTitles中的值有效。
        /// </summary>
        /// <param name="basePt">左上角的起始坐标。</param>
        internal void LocateAtRight(Point basePt)
        {
            basePointOfControlRect.X = basePt.X;
            basePointOfControlRect.Y = basePt.Y;

            double indentWidth = this.IndentWidth;

            //先定自身位置
            leftTop.X = basePt.X;

            Title higherTitle = this.GetHigherLvelTitle();
            if (higherTitle != null)
            {
                List<Title> brothers = higherTitle.GetLowerLevelTitles();
                if (brothers.Count == 1 && brothers[0] == this && higherTitle.ActualHeight == this.ActualHeight)
                {
                    leftTop.Y = higherTitle.LeftTop.Y;
                }
                else
                {
                    leftTop.Y = basePt.Y + (this.needSize.Height - this.ActualHeight) / 2;
                }
            }
            else
            {
                leftTop.Y = basePt.Y + (this.needSize.Height - this.ActualHeight) / 2;
            }

            double newLeft = leftTop.X + indentWidth;
            if (Canvas.GetLeft(this) != newLeft)
            {
                Canvas.SetLeft(this, newLeft);
            }

            double newTop = leftTop.Y;
            if (Canvas.GetTop(this) != newTop)
            {
                Canvas.SetTop(this, leftTop.Y);
            }

            ResetDirection(TitleDirection.Right);

            double baseLeft = basePt.X + indentWidth + this.ActualWidth + blankAreaWidth;
            double baseTop = basePt.Y + baseTopAddValueFromSummary + baseTopAddValueWhenHeightBigThanLowersHeights;

            for (int i = 0; i < lowerLevelTitles.Count; i++)
            {
                Title t = lowerLevelTitles[i];

                t.LocateAtRight(new Point(baseLeft, baseTop));
                if (t.Direction != TitleDirection.Right)
                {
                    t.Direction = TitleDirection.Right;
                }
                baseTop += t.NeedSize.Height;
            }

            RefreshOutBorderVisibled();
            RefreshAutoNumberString();//默认视图本身不需要自动编号，但是需要取消自动编号
        }

        #endregion

        #region 绘制关系线，这仨方法也只应调用其中一个。

        internal void DrawListRelationLine(PathGeometry pg)
        {
            if (LowerLevelTitles.Count <= 0 || CollapsedLowers) return;

            Point startPt = FormatPoint(new Point(Canvas.GetLeft(this) + Globals.BlankAreaWidth / 2,
                Canvas.GetTop(this) + this.ActualHeight));

            double endY = 0;

            for (int i = 0; i < lowerLevelTitles.Count; i++)
            {
                PathFigure pf = new PathFigure() { IsClosed = false };
                Title t = lowerLevelTitles[i];

                endY = Canvas.GetTop(t) + t.ActualHeight;
                pf.StartPoint = FormatPoint(new Point(startPt.X, endY));

                LineSegment ls = new LineSegment(FormatPoint(
                    new Point(pf.StartPoint.X + t.ActualWidth + Globals.BlankAreaWidth / 2,
                        pf.StartPoint.Y)), true);
                pf.Segments.Add(ls);
                pg.Figures.Add(pf);

                t.DrawListRelationLine(pg);//递归
            }

            //画竖线。
            PathFigure pfVertical = new PathFigure() { IsClosed = false };
            pfVertical.StartPoint = startPt;

            LineSegment lsVertical = new LineSegment(FormatPoint(new Point(startPt.X, endY)), true);
            pfVertical.Segments.Add(lsVertical);
            pg.Figures.Add(pfVertical);
        }

        /// <summary>
        /// 向左绘制关系线。
        /// </summary>
        /// <param name="pathFigureCollection"></param>
        /// <param name="basePoint">基准点。对于左侧标题来说，是下侧直线的右端点。</param>
        internal void DrawLeftLine(PathFigureCollection pathFigureCollection, Point basePoint)
        {
            double indentWidth = this.IndentWidth;

            Point startPoint = new Point(
                LeftTop.X - indentWidth,//左边缘
                basePoint.Y//下边缘
                );

            //控制点１
            Point ctrlPt1 = new Point(
                startPoint.X - blankAreaWidth / 4 * 3,
                startPoint.Y
                );

            //控制点２由各下级标题自行决定。

            if (beCollapsed == false && collapsedLowers == false)
            {
                foreach (Title t in lowerLevelTitles)
                {
                    PathFigure pf = new PathFigure() { StartPoint = startPoint, IsFilled = false };

                    Point ctrlPt2 = new Point(
                        /*ctrlPt1.X*/startPoint.X - blankAreaWidth / 4,
                        t.LeftTop.Y + t.ActualHeight - 2
                        );

                    Point endPt = new Point(
                        startPoint.X - blankAreaWidth,
                        ctrlPt2.Y);

                    BezierSegment bs = new BezierSegment(ctrlPt1, ctrlPt2, endPt, true);
                    pf.Segments.Add(bs);

                    pathFigureCollection.Add(pf);

                    //递归
                    t.DrawLeftLine(pathFigureCollection, endPt);
                }
            }

            if (beCollapsed == false)
            {
                //再在自身下底端画条直线
                Point sLineEndPt = basePoint;
                PathFigure pfSLine = new PathFigure() { StartPoint = startPoint };
                pfSLine.Segments.Add(new LineSegment(sLineEndPt, true));
                pathFigureCollection.Add(pfSLine);

            }
        }

        public Point FormatPoint(Point src)
        {
            src.X = (int)src.X;
            src.Y = (int)src.Y;
            return src;
        }

        /// <summary>
        /// 向右绘制关系线。
        /// </summary>
        /// <param name="pathFigureCollection"></param>
        /// <param name="basePoint">对于右侧的标题来说，此参数指下侧线的左端点。</param>
        internal void DrawRightLine(PathFigureCollection pathFigureCollection, Point basePoint)
        {
            double indentWidth = this.IndentWidth;

            Point startPoint = new Point(
                LeftTop.X + indentWidth + this.ActualWidth,//右边缘
                basePoint.Y//下边缘
                );

            //控制点１
            Point ctrlPt1 = new Point(
                startPoint.X + blankAreaWidth / 4 * 3,
                startPoint.Y
                );

            //控制点２由各下级标题自行决定。

            if (beCollapsed == false && collapsedLowers == false)
            {
                foreach (Title t in lowerLevelTitles)
                {
                    PathFigure pf = new PathFigure() { StartPoint = startPoint, IsFilled = false };

                    Point ctrlPt2 = new Point(
                        /*ctrlPt1.X*/startPoint.X + blankAreaWidth / 4,
                        t.LeftTop.Y + t.ActualHeight - 2
                        );

                    Point endPt = new Point(
                        startPoint.X + blankAreaWidth,
                        ctrlPt2.Y);

                    BezierSegment bs = new BezierSegment(ctrlPt1, ctrlPt2, endPt, true);
                    pf.Segments.Add(bs);

                    pathFigureCollection.Add(pf);

                    //递归
                    t.DrawRightLine(pathFigureCollection, endPt);
                }
            }

            if (beCollapsed == false)
            {
                //再在自身下底端画条直线
                Point sLineEndPt = basePoint;
                PathFigure pfSLine = new PathFigure() { StartPoint = startPoint };
                pfSLine.Segments.Add(new LineSegment(sLineEndPt, true));
                pathFigureCollection.Add(pfSLine);

                //画个圈，但是效果不好。2014年12月25日
                //if (this.Editor != null)
                //{
                //    if (this.Editor.RootTitle == this.GetHigherLvelTitle())
                //    {
                //        if (this.CollapsedLowers == true || this.GetLowerLevelTitles().Count <= 0)
                //        {
                //            //如果是一级标题，再画个圈。
                //            var pathFigureCircle = new PathFigure();
                //            pathFigureCircle.StartPoint = startPoint;
                //            pathFigureCircle.IsFilled = false;

                //            var circle = new BezierSegment();
                //            circle.IsSmoothJoin = false;
                //            circle.Point1 = new Point(this.LeftTop.X + this.ActualWidth * 2, this.LeftTop.Y - this.ActualHeight);
                //            circle.Point2 = new Point(this.LeftTop.X - this.ActualWidth * 2, this.LeftTop.Y - this.ActualHeight);
                //            circle.Point3 = new Point(this.LeftTop.X, basePoint.Y + this.ActualHeight / 2);
                //            pathFigureCircle.Segments.Add(circle);
                //            pathFigureCollection.Add(pathFigureCircle);
                //        }
                //    }
                //}
            }
        }

        #endregion

        private bool beCollapsed = false;

        /// <summary>
        ///表示被上级给折叠了。
        /// </summary>
        public bool BeCollapsed
        {
            get { return beCollapsed; }
            set
            {
                beCollapsed = value;
                if (this.beCollapsed)
                {
                    this.Visibility = Visibility.Hidden;
                }
                else
                {
                    this.Visibility = Visibility.Visible;
                }
            }
        }

        protected TitleCollapseAdorner collapseAdorner;

        public void RefreshCollapseAdorner()
        {
            if (this.collapseAdorner == null) return;

            this.collapseAdorner.InvalidateVisual();


            if (this.collapsedLowers == false)
            {
                var lowers = this.GetLowerLevelTitles();
                foreach (var lowt in lowers)
                {
                    if (lowt.CollapsedLowers)
                    {
                        //这段代码最初似乎是为了解决装饰器状态不正确的问题而添加的，
                        //但添加下面这行后却造成一些“折叠全部标题、展开全部标题”这两个功能失效。
                        //lowt.RefreshNeedSize(false);//这个方法不能随意调用！！！
                        lowt.IsNeedSizeInvalid = true;
                        lowt.InvalidateMeasure();
                        lowt.UpdateLayout();
                        lowt.RefreshCollapseAdorner();
                        lowt.RefreshAddLowerLevelTitleAdorner();
                        lowt.RefreshAddSameLevelTitleAdorner();
                    }
                }
            }
        }

        protected AddSameLevelTitleAdorner addSameLevelTitleAdorner;

        public void RefreshAddSameLevelTitleAdorner()
        {
            if (this.addSameLevelTitleAdorner == null) return;

            this.addSameLevelTitleAdorner.InvalidateVisual();
        }

        protected AddLowerLevelTitleAdorner addLowerLevelTitleAdorner;

        public void RefreshAddLowerLevelTitleAdorner()
        {
            if (this.addLowerLevelTitleAdorner == null) return;

            this.addLowerLevelTitleAdorner.InvalidateVisual();
        }

        protected bool isFixed = false;

        /// <summary>
        /// FixedTitle类从此类派生，其IsFixed属性值为true，其位置类似Comment,
        /// 根据上级Title来决定。
        /// </summary>
        public bool IsFixed
        {
            get { return isFixed; }
            set
            {
                isFixed = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("IsFixed", value.ToString());
                }

                OnPropertyChanged(this, new PropertyChangedEventArgs("IsFixed"));
            }
        }

        private bool collapsedLowers = false;

        /// <summary>
        /// 折叠所有下级。
        /// </summary>
        public bool CollapsedLowers
        {
            get { return collapsedLowers; }
            set
            {
                if (higherLevelTitleID != null && higherLevelTitleID.Length > 0)
                {
                    collapsedLowers = value;
                }
                else return;//根标题永远不折叠下级。

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("CollapsedLowers", collapsedLowers.ToString());
                }

                RefreshCollapseAdorner();
            }
        }

        protected Dock textDock = Dock.Right;

        /// <summary>
        /// 指定文本面板在图像面板的哪一侧。默认为右侧。
        /// </summary>
        public Dock TextDock
        {
            get { return textDock; }
            set
            {
                textDock = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("TextDock", value.ToString());
                }

                RefreshTextDock();
            }
        }

        public void RefreshTextDock()
        {
            switch (textDock)
            {
                case Dock.Left:
                    {
                        DockPanel.SetDock(this.textStackPanel, Dock.Left);
                        DockPanel.SetDock(this.imgStackPanel, Dock.Right);
                        this.imgStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
                        this.textStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
                        break;
                    }
                case Dock.Top:
                    {
                        DockPanel.SetDock(this.textStackPanel, Dock.Top);
                        DockPanel.SetDock(this.imgStackPanel, Dock.Bottom);
                        this.imgStackPanel.HorizontalAlignment = HorizontalAlignment.Center;
                        this.textStackPanel.HorizontalAlignment = HorizontalAlignment.Center;
                        break;
                    }
                case Dock.Bottom:
                    {
                        DockPanel.SetDock(this.textStackPanel, Dock.Bottom);
                        DockPanel.SetDock(this.imgStackPanel, Dock.Top);
                        this.imgStackPanel.HorizontalAlignment = HorizontalAlignment.Center;
                        this.textStackPanel.HorizontalAlignment = HorizontalAlignment.Center;
                        break;
                    }
                default:
                    {
                        DockPanel.SetDock(this.textStackPanel, Dock.Right);
                        DockPanel.SetDock(this.imgStackPanel, Dock.Left);
                        this.imgStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
                        this.textStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
                        break;
                    }
            }

            this.IsNeedSizeInvalid = false;
        }

        /// <summary>
        /// 一次公式级的撤销操作。
        /// </summary>
        /// <param name="action">一次撤销或重做可以涉及到多个公式（因而包含多次公式级的撤销或重做）。</param>
        public virtual void Undo(Action action)
        {
            if (action != null)
            {
                SetProperty(action.PropertyOrActionTypeName, action.OldValue);
            }
        }

        /// <summary>
        /// 一次公式级的重做操作。
        /// </summary>
        /// <param name="action"></param>
        public virtual void Redo(Action action)
        {
            if (action != null)
            {
                SetProperty(action.PropertyOrActionTypeName, action.NewValue);
            }
        }

        public virtual void SetProperty(string propertyName, string value)
        {
            switch (propertyName)
            {
                case "TextDock":
                    {
                        this.TextDock = (Dock)Enum.Parse(typeof(Dock), value);
                        RefreshTextDock();
                        break;
                    }
                case "IsFixed":
                    {
                        this.IsFixed = bool.Parse(value);
                        break;
                    }
                case "Index":
                    {
                        this.Index = int.Parse(value);
                        break;
                    }
                case "Indent":
                    {
                        this.Indent = int.Parse(value);
                        break;
                    }
                case "XmlDataInnerXml":
                    {
                        XmlNode textSetNode = this.xmlData.SelectSingleNode("TextSet");
                        if (textSetNode == null)
                        {
                            textSetNode = this.xmlData.AppendXml("<TextSet />");
                        }

                        if (textSetNode == null)
                        {
                            MessageBox.Show("发生意外，没有找到标题的TextSet节点，且无法添加。编辑标题文本失败。",
                                Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
                            return;
                        }

                        textSetNode.InnerXml = value;
                        ReadTitleTextFromXmlData(this.XmlData, this.mainTextBlock, this);
                        break;
                    }
                case "XmlCommentInnerXml":
                    {
                        XmlNode commentSetNode = this.xmlData.SelectSingleNode("CommentSet");
                        if (commentSetNode == null)
                        {
                            commentSetNode = this.xmlData.AppendXml("<CommentSet />");
                        }

                        if (commentSetNode == null)
                        {
                            MessageBox.Show("发生意外，没有找到标题的CommentSet节点，且无法添加。编辑标题备注文本失败。",
                                Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
                            return;
                        }

                        commentSetNode.InnerXml = value;
                        ReadCommentTextFromXmlData(this.XmlData, this.commentTextBlock, this);

                        RefreshCommentTextVisible();

                        break;
                    }
                case "BorderColor":
                    {
                        if (value == "UnKnown")
                        {
                            this.BorderColor = null;
                        }
                        else
                        {
                            this.BorderColor = ColorPicker.GetBrushByName(value);
                        }
                        break;
                    }
                case "BorderWidth":
                    {
                        this.BorderWidth = int.Parse(value);
                        break;
                    }
                #region 已不需要此属性，2011年11月20日
                //case "CommentText":
                //    {
                //        this.CommentText = value;
                //        break;
                //    } 
                #endregion
                case "TitleBackground":
                    {
                        this.TitleBackground = ColorPicker.GetBrushByName(value);
                        break;
                    }
                case "TitleForeground":
                    {
                        this.TitleForeground = ColorPicker.GetBrushByName(value);
                        break;
                    }
                case "RelationLineStroke":
                    {
                        this.RelationLineStroke = ColorPicker.GetBrushByName(value);
                        break;
                    }
                case "Direction":
                    {
                        this.Direction = (TitleDirection)Enum.Parse(typeof(TitleDirection), value);
                        break;
                    }
                case "RotateAngle":
                    {
                        this.RotateAngle = double.Parse(value); break;
                    }
                case "HigherLevelTitleID":
                    {
                        this.HigherLevelTitleID = value; break;
                    }
                case "CollapsedLowers":
                    {
                        this.CollapsedLowers = bool.Parse(value); break;
                    }
                case "IsOutBorderVisibled":
                    {
                        this.IsOutBorderVisibled = bool.Parse(value); break;
                    }
                case "QuestionText":
                    {
                        this.QuestionText = value; break;
                    }
                case "IconXamlText":
                    {
                        this.IconXamlText = value; break;
                    }
            }
        }

        public int TitleImagesCount
        {
            get
            {
                if (this.imgStackPanel == null) return 0;
                return this.imgStackPanel.Children.Count;
            }
        }

        public List<TitleImage> ResourceLinkImages
        {
            get
            {
                var list = new List<TitleImage>();
                foreach (var child in this.imgStackPanel.Children)
                {
                    if (child is TitleImage titleImage)
                    {
                        if (string.IsNullOrWhiteSpace(titleImage.Link) == false)
                        {
                            list.Add(titleImage);
                        }
                    }
                }
                return list;
            }
        }

        internal void PresentateImages()
        {
            if (this.imgStackPanel.Children.Count <= 0)
            {
                this.ImagesPresentated = true;
                return;//没嵌入图标，不必显示。
            }

            ImagePresentationWindow ipw = new ImagePresentationWindow() { Owner = Globals.MainWindow, };
            ipw.backgroundRect.Fill = Globals.MainWindow.mainCanvas.Background;

            ipw.TitleImages.Clear();
            foreach (var child in this.imgStackPanel.Children)
            {
                if (child is TitleImage titleImage)
                {
                    ipw.TitleImages.Add(titleImage);
                }
            }
            Globals.SwitchInputMethod(false);
            ipw.ShowDialog();
            Globals.SwitchInputMethod(true);
            ipw.Closed += Ipw_Closed;
        }

        private void Ipw_Closed(object sender, EventArgs e)
        {
            this.ImagesPresentated = true;
        }

        public bool HasQuestion
        {
            get
            {
                string questionText = XmlTools.RestoreXmlChars(this.QuestionText);
                return QuestionValidateManager.ValidateForPresentation(questionText, null);
            }
        }

        public void PresentateQuestions()
        {
            string questionText = XmlTools.RestoreXmlChars(this.QuestionText);
            StringBuilder errorMsg = new StringBuilder();
            if (QuestionValidateManager.ValidateForPresentation(questionText, errorMsg) == false)
            {

                MessageBox.Show("文件未通过合法性验证，错误消息如下：\r\n=========××××========\r\n" + errorMsg.ToString(), Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            PresentationWindow pw = new PresentationWindow()
            {
                Owner = Globals.MainWindow,
                WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner,
                WindowState = WindowState.Maximized,
                ContentText = questionText,
            };

            pw.BuildQuestions(questionText);

            Globals.SwitchInputMethod(false);
            pw.ShowDialog();
            Globals.SwitchInputMethod(true);
        }

        /// <summary>
        /// [只读]标题主文本区域中是否有文本片段存在下划线。
        /// </summary>
        public bool HasFillBlank
        {
            get
            {
                foreach (var s in this.mainTextBlock.Inlines)
                {
                    Span span = s as Span;
                    if (span == null) continue;

                    foreach (var td in span.TextDecorations)
                    {
                        if (td.Location == TextDecorationLocation.Underline)
                        {
                            return true;
                        }
                    }
                }

                //2013年10月6日，目前“填空模式”还未作用于TitleComment。暂时不需要这段。
                //foreach (var s in this.commentTextBlock.Inlines)
                //{
                //    Span span = s as Span;
                //    if (span == null) continue;

                //    foreach (var td in span.TextDecorations)
                //    {
                //        if (td.Location == TextDecorationLocation.Underline)
                //        {
                //            return true;
                //        }
                //    }
                //}

                return false;
            }
        }

        public bool IsNoneFillBlankVisible
        {
            get
            {
                foreach (var item in this.mainTextBlock.Inlines)
                {
                    var span = item as Span;
                    if (span == null || span == this.autoNumberSpan) continue;

                    if (span.Tag != null && span.Tag.ToString() == "FillBlank")
                    {
                        foreach (var item2 in span.Inlines)
                        {
                            var run = item2 as Run;
                            if (run == null) continue;

                            if (run.Foreground == Brushes.Transparent)
                            {
                                continue;
                            }
                            else return false;
                        }
                    }
                }

                return true;
            }
        }

        public XmlNode ImageSetNode
        {
            get
            {
                if (this.XmlData == null) return null;
                return this.XmlData.SelectSingleNode("ImageSet");
            }
        }

        public bool ImagesPresentated { get; set; }

        public bool HasTitleImage
        {
            get
            {
                var count = 0;
                foreach (UIElement ue in this.imgStackPanel.Children)
                {
                    if (ue is TitleImage)
                    {
                        count++;
                    }
                }

                return count > 0;
            }
        }

        internal bool ShowNextFillBlankTextOrPrentatateImages()
        {
            if (editor == null) return false;

            if (editor.tbtnFullScreen.IsChecked != true) return false;

            Span nextTransparentFillBlankSpan = null;
            Run nextTransparentFillBlankRun = null;

            foreach (var item in this.mainTextBlock.Inlines)
            {
                var span = item as Span;
                if (span == null || span == this.autoNumberSpan) continue;

                if (span.Tag != null && span.Tag.ToString() == "FillBlank")
                {
                    foreach (var item2 in span.Inlines)
                    {
                        var run = item2 as Run;
                        if (run == null) continue;

                        if (run.Foreground == Brushes.Transparent)
                        {
                            nextTransparentFillBlankRun = run;
                            break;
                        }
                    }

                    if (nextTransparentFillBlankRun != null)
                    {
                        nextTransparentFillBlankSpan = span;
                        break;
                    }
                }
            }

            if (nextTransparentFillBlankSpan != null)
            {
                foreach (var item2 in nextTransparentFillBlankSpan.Inlines)
                {
                    var run = item2 as Run;
                    if (run != null) run.Foreground = Brushes.Red;
                }
                return true;
            }
            else
            {
                if (this.HasTitleImage && this.ImagesPresentated == false)
                {
                    this.PresentateImages();
                    this.ImagesPresentated = true;
                }
                this.IsFillBlankVisible = true;
                return false;
            }
        }
    }

    public class CustomSpan
    {
        public Span Span { get; set; }
        public bool IsUnderLine { get; set; }
    }

    /// <summary>
    /// 根据全局索引对标题进行排序。
    /// </summary>
    public class GlobalIndexComparer : IComparer<Title>
    {
        public int Compare(Title x, Title y)
        {
            return x.GlobalIndex.CompareTo(y.GlobalIndex);
        }
    }
}
